From 912e4a8d6edea1f2fc335f9874d793fd10de811c Mon Sep 17 00:00:00 2001 From: Nikola Radakovic Date: Tue, 14 Jul 2026 16:29:25 +0200 Subject: [PATCH 1/5] Implement explicit features under no_legacy_featur Enable no_legacy_features on the Linux and QNX cc_toolchain configs and explicitly implement every compile, link, and archive feature previously provided by Bazel's legacy machinery, plus guarded legacy features enabled by default (fully_static_link stays opt-in). Also fix include_paths to use the correct build variables (quote_include_paths/include_paths/system_include_paths), which were never populated before and so emitted no -iquote/-I/-isystem flags. resolves #76 #77 --- .../linux/cc_toolchain_config.bzl.template | 576 ++++++++++++++++++ .../qnx/cc_toolchain_config.bzl.template | 337 ++++++++++ 2 files changed, 913 insertions(+) diff --git a/templates/linux/cc_toolchain_config.bzl.template b/templates/linux/cc_toolchain_config.bzl.template index 8ee5a75..2ac5196 100644 --- a/templates/linux/cc_toolchain_config.bzl.template +++ b/templates/linux/cc_toolchain_config.bzl.template @@ -24,6 +24,7 @@ load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "flag_set", "tool", "tool_path", + "variable_with_value", "with_feature_set", ) load("@rules_cc//cc:defs.bzl", "cc_common", "CcToolchainConfigInfo") @@ -85,6 +86,8 @@ def _impl(ctx): dbg_feature = feature(name = "dbg") opt_feature = feature(name = "opt") + no_legacy_features_feature = feature(name = "no_legacy_features", enabled = True) + # Temp solution until we do not add feature injection gnu11_feature = feature(name = "gnu11") @@ -605,6 +608,554 @@ def _impl(ctx): supports_pic_feature = feature(name = "supports_pic", enabled = True) + pic_feature = feature( + name = "pic", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group(flags = ["-fPIC"], expand_if_available = "pic"), + ], + ), + ], + ) + + user_compile_flags_feature = feature( + name = "user_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "user_compile_flags", + flags = ["%{user_compile_flags}"], + expand_if_available = "user_compile_flags", + ), + ], + ), + ], + ) + + random_seed_feature = feature( + name = "random_seed", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + expand_if_available = "output_file", + flags = ["-frandom-seed=%{output_file}"], + ), + ], + ), + ], + ) + + include_paths_feature = feature( + name = "include_paths", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "quote_include_paths", + flags = ["-iquote", "%{quote_include_paths}"], + expand_if_available = "quote_include_paths", + ), + ], + ), + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "include_paths", + flags = ["-I%{include_paths}"], + expand_if_available = "include_paths", + ), + ], + ), + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "system_include_paths", + flags = ["-isystem", "%{system_include_paths}"], + expand_if_available = "system_include_paths", + ), + ], + ), + ], + ) + + preprocessor_defines_feature = feature( + name = "preprocessor_defines", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "preprocessor_defines", + flags = ["-D%{preprocessor_defines}"], + expand_if_available = "preprocessor_defines", + ), + ], + ), + ], + ) + + compiler_input_flags_feature = feature( + name = "compiler_input_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = ["-c", "%{source_file}"], + expand_if_available = "source_file", + ), + ], + ), + ], + ) + + dependency_file_feature = feature( + name = "dependency_file", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.objc_compile, + ACTION_NAMES.objcpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = ["-MD", "-MF", "%{dependency_file}"], + expand_if_available = "dependency_file", + ), + ], + ), + ], + ) + + compiler_output_flags_feature = feature( + name = "compiler_output_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = ["-S"], + expand_if_available = "output_assembly_file", + ), + flag_group( + flags = ["-E"], + expand_if_available = "output_preprocess_file", + ), + flag_group( + flags = ["-o", "%{output_file}"], + expand_if_available = "output_file", + ), + ], + ), + ], + ) + + archiver_flags_feature = feature( + name = "archiver_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group(flags = ["rcsD", "%{output_execpath}"]), + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + flag_group( + iterate_over = "user_archiver_flags", + flags = ["%{user_archiver_flags}"], + expand_if_available = "user_archiver_flags", + ), + ], + ), + ], + ) + + linker_param_file_feature = feature( + name = "linker_param_file", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions + [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group( + flags = ["@%{linker_param_file}"], + expand_if_available = "linker_param_file", + ), + ], + ), + ], + ) + + library_search_directories_feature = feature( + name = "library_search_directories", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "library_search_directories", + flag_groups = [ + flag_group( + flags = ["-L%{library_search_directories}"], + ), + ], + expand_if_available = "library_search_directories", + ), + ], + ), + ], + ) + + shared_flag_feature = feature( + name = "shared_flag", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [flag_group(flags = ["-shared"])], + ), + ], + ) + + output_execpath_flags_feature = feature( + name = "output_execpath_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-o", "%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + ], + ) + + runtime_library_search_directories_feature = feature( + name = "runtime_library_search_directories", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "runtime_library_search_directories", + flag_groups = [ + flag_group( + flags = ["-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}"], + expand_if_true = "is_cc_test", + ), + flag_group( + flags = ["-Wl,-rpath,$EXEC_ORIGIN/%{runtime_library_search_directories}"], + expand_if_false = "is_cc_test", + ), + ], + expand_if_available = "runtime_library_search_directories", + ), + ], + ), + ], + ) + + libraries_to_link_feature = feature( + name = "libraries_to_link", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["-Wl,--whole-archive", "%{libraries_to_link.name}", "-Wl,--no-whole-archive"], + expand_if_true = "libraries_to_link.is_whole_archive", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "static_library", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_false = "libraries_to_link.is_whole_archive", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "static_library", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flags = ["-l%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "dynamic_library", + ), + ), + flag_group( + flags = ["-l:%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "versioned_dynamic_library", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + ], + ), + ], + ) + + user_link_flags_feature = feature( + name = "user_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "user_link_flags", + flags = ["%{user_link_flags}"], + expand_if_available = "user_link_flags", + ), + ], + ), + ], + ) + + # ------------------------------------------------------------------------ + # Additional legacy features. + # + # These mirror features that Bazel's legacy toolchain machinery would add + # automatically. All except fully_static_link are guarded (via + # expand_if_available or with_features) so they are no-ops for normal builds + # and only emit flags when the relevant build mode is active (e.g. --fission, + # --force_pic, --stamp). They are therefore enabled by default. + # + # fully_static_link is left DISABLED by default because it emits -static + # unconditionally, which would force fully static linking for every target. + # Users who want it must opt in explicitly, e.g. via `--features=`. + # ------------------------------------------------------------------------ + + # Fission: emit split DWARF debug info into separate .dwo files at compile. + per_object_debug_info_feature = feature( + name = "per_object_debug_info", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + flags = ["-gsplit-dwarf", "-g"], + expand_if_available = "per_object_debug_info_file", + ), + ], + ), + ], + ) + + # Fission: emit a .gdb_index section at link when fission is in use. + fission_support_feature = feature( + name = "fission_support", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-Wl,--gdb-index"], + expand_if_available = "is_using_fission", + ), + ], + ), + ], + ) + + # Pass linkstamp object files to the linker (used with --stamp). + linkstamps_feature = feature( + name = "linkstamps", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "linkstamp_paths", + flags = ["%{linkstamp_paths}"], + expand_if_available = "linkstamp_paths", + ), + ], + ), + ], + ) + + # Force-include headers via -include (the `includes` build variable). + includes_feature = feature( + name = "includes", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "includes", + flags = ["-include", "%{includes}"], + expand_if_available = "includes", + ), + ], + ), + ], + ) + + # Build position-independent executables under --force_pic. + force_pic_flags_feature = feature( + name = "force_pic_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.cpp_link_executable], + flag_groups = [ + flag_group( + flags = ["-pie"], + expand_if_available = "force_pic", + ), + ], + ), + ], + ) + + # Strip debug symbols at link time. + strip_debug_symbols_feature = feature( + name = "strip_debug_symbols", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-Wl,-S"], + expand_if_available = "strip_debug_symbols", + ), + ], + ), + ], + ) + + # Statically link libgcc (paired with static_link_cpp_runtimes). + static_libgcc_feature = feature( + name = "static_libgcc", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ], + flag_groups = [flag_group(flags = ["-static-libgcc"])], + with_features = [ + with_feature_set(features = ["static_link_cpp_runtimes"]), + ], + ), + ], + ) + + # Fully static link (-static) under --dynamic_mode=fully. + fully_static_link_feature = feature( + name = "fully_static_link", + enabled = False, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [flag_group(flags = ["-static"])], + ), + ], + ) + # Feature to disable absolute path warnings for system headers supports_header_path_normalization = feature( name = "supports_header_path_normalization", @@ -641,6 +1192,7 @@ def _impl(ctx): # after a command line parameter from a feature at the beginning of the list. features = [ + no_legacy_features_feature, compiler_library_search_paths_feature, sanitizer_feature, asan_feature, @@ -651,7 +1203,30 @@ def _impl(ctx): unfiltered_compile_flags_feature, gnu11_feature, default_compile_flags_feature, + random_seed_feature, + include_paths_feature, + preprocessor_defines_feature, + user_compile_flags_feature, + compiler_input_flags_feature, + compiler_output_flags_feature, + dependency_file_feature, + per_object_debug_info_feature, + includes_feature, default_link_flags_feature, + archiver_flags_feature, + linker_param_file_feature, + library_search_directories_feature, + shared_flag_feature, + output_execpath_flags_feature, + runtime_library_search_directories_feature, + libraries_to_link_feature, + user_link_flags_feature, + linkstamps_feature, + fission_support_feature, + force_pic_flags_feature, + strip_debug_symbols_feature, + static_libgcc_feature, + fully_static_link_feature, sysroot_link_flags_feature, pthread_feature, minimal_warnings_feature, @@ -665,6 +1240,7 @@ def _impl(ctx): opt_feature, supports_dynamic_linker_feature, supports_pic_feature, + pic_feature, supports_header_path_normalization, supports_fission_feature, coverage_feature, diff --git a/templates/qnx/cc_toolchain_config.bzl.template b/templates/qnx/cc_toolchain_config.bzl.template index 97b6d8b..2c89b31 100644 --- a/templates/qnx/cc_toolchain_config.bzl.template +++ b/templates/qnx/cc_toolchain_config.bzl.template @@ -24,6 +24,7 @@ load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "flag_set", "tool", "tool_path", + "variable_with_value", "with_feature_set", ) load("@rules_cc//cc:defs.bzl", "cc_common", "CcToolchainConfigInfo") @@ -86,6 +87,8 @@ def _impl(ctx): dbg_feature = feature(name = "dbg") opt_feature = feature(name = "opt") + no_legacy_features_feature = feature(name = "no_legacy_features", enabled = True) + supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True) assemble_action = action_config( @@ -160,6 +163,326 @@ def _impl(ctx): strip_action, ] + # Core compilation features + random_seed_feature = feature( + name = "random_seed", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + expand_if_available = "output_file", + flags = ["-frandom-seed=%{output_file}"], + ), + ], + ), + ], + ) + + include_paths_feature = feature( + name = "include_paths", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "quote_include_paths", + flags = ["-iquote", "%{quote_include_paths}"], + expand_if_available = "quote_include_paths", + ), + ], + ), + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "include_paths", + flags = ["-I%{include_paths}"], + expand_if_available = "include_paths", + ), + ], + ), + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "system_include_paths", + flags = ["-isystem", "%{system_include_paths}"], + expand_if_available = "system_include_paths", + ), + ], + ), + ], + ) + + preprocessor_defines_feature = feature( + name = "preprocessor_defines", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "preprocessor_defines", + flags = ["-D%{preprocessor_defines}"], + expand_if_available = "preprocessor_defines", + ), + ], + ), + ], + ) + + user_compile_flags_feature = feature( + name = "user_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + iterate_over = "user_compile_flags", + flags = ["%{user_compile_flags}"], + expand_if_available = "user_compile_flags", + ), + ], + ), + ], + ) + + compiler_input_flags_feature = feature( + name = "compiler_input_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = ["-c", "%{source_file}"], + expand_if_available = "source_file", + ), + ], + ), + ], + ) + + compiler_output_flags_feature = feature( + name = "compiler_output_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = ["-S"], + expand_if_available = "output_assembly_file", + ), + flag_group( + flags = ["-E"], + expand_if_available = "output_preprocess_file", + ), + flag_group( + flags = ["-o", "%{output_file}"], + expand_if_available = "output_file", + ), + ], + ), + ], + ) + + archiver_flags_feature = feature( + name = "archiver_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group(flags = ["rcsD", "%{output_execpath}"]), + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + flag_group( + iterate_over = "user_archiver_flags", + flags = ["%{user_archiver_flags}"], + expand_if_available = "user_archiver_flags", + ), + ], + ), + ], + ) + + # Core linking features + user_link_flags_feature = feature( + name = "user_link_flags_feature", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "user_link_flags", + flags = ["%{user_link_flags}"], + expand_if_available = "user_link_flags", + ), + ], + ), + ], + ) + + linker_param_file_feature = feature( + name = "linker_param_file", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions + [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group( + flags = ["@%{linker_param_file}"], + expand_if_available = "linker_param_file", + ), + ], + ), + ], + ) + + library_search_directories_feature = feature( + name = "library_search_directories", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "library_search_directories", + flag_groups = [ + flag_group( + flags = ["-L%{library_search_directories}"], + ), + ], + expand_if_available = "library_search_directories", + ), + ], + ), + ], + ) + + shared_flag_feature = feature( + name = "shared_flag", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [flag_group(flags = ["-shared"])], + ), + ], + ) + + output_execpath_flags_feature = feature( + name = "output_execpath_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-o", "%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + ], + ) + + libraries_to_link_feature = feature( + name = "libraries_to_link", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["-Wl,--whole-archive", "%{libraries_to_link.name}", "-Wl,--no-whole-archive"], + expand_if_true = "libraries_to_link.is_whole_archive", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "static_library", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_false = "libraries_to_link.is_whole_archive", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "static_library", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flags = ["-l%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "dynamic_library", + ), + ), + flag_group( + flags = ["-l:%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "versioned_dynamic_library", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + ], + ), + ], + ) + unfiltered_compile_flags_feature = feature( name = "unfiltered_compile_flags", enabled = True, @@ -456,11 +779,25 @@ def _impl(ctx): features = [ dbg_feature, + no_legacy_features_feature, unfiltered_compile_flags_feature, default_compile_flags_feature, + random_seed_feature, + include_paths_feature, + preprocessor_defines_feature, + user_compile_flags_feature, + compiler_input_flags_feature, + compiler_output_flags_feature, dependency_file_named_implicitly_feature, dependency_file_feature, default_link_flags_feature, + archiver_flags_feature, + user_link_flags_feature, + linker_param_file_feature, + library_search_directories_feature, + shared_flag_feature, + output_execpath_flags_feature, + libraries_to_link_feature, minimal_warnings_feature, strict_warnings_feature, all_wall_warnings_feature, From 1e3daa9b8ae690f117105e9bafa7fd62b5e340ca Mon Sep 17 00:00:00 2001 From: Nikola Radakovic Date: Tue, 14 Jul 2026 16:51:31 +0200 Subject: [PATCH 2/5] Consolidate toolchain feature documentation Rewrite docs/features.md to reflect the explicit, no_legacy_features-based feature set of both the Linux and QNX toolchains, with per-feature platform tags and default-enabled/opt-in status. Remove the two development/planning documents (linux_toolchain_feature_set.md, qnx_toolchain_feature_set.md) now that the migration they tracked is complete, migrating their still-relevant sysroot and tool-wiring notes into a new "Toolchain Feature Wiring" section in docs/maintenance.md. Update the README to link to features.md instead of the removed documents. resolves #80 --- README.md | 3 +- docs/features.md | 113 +++++++++++++++++ docs/linux_toolchain_feature_set.md | 179 --------------------------- docs/maintenance.md | 36 ++++++ docs/qnx_toolchain_feature_set.md | 184 ---------------------------- 5 files changed, 150 insertions(+), 365 deletions(-) create mode 100644 docs/features.md delete mode 100644 docs/linux_toolchain_feature_set.md delete mode 100644 docs/qnx_toolchain_feature_set.md diff --git a/README.md b/README.md index 7101af4..1dc5c63 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,7 @@ validates the setup, and how QNX-specific authentication and licensing fit in. - [Repository layout](docs/repository_layout.md) - [Extension API](docs/extension_api.md) - [Generation flow](docs/generation_flow.md) -- [Linux toolchain feature set](docs/linux_toolchain_feature_set.md) -- [QNX toolchain feature set](docs/qnx_toolchain_feature_set.md) +- [Toolchain features](docs/features.md) - [Tests and validation](docs/tests_and_validation.md) - [QNX integration](docs/qnx_integration.md) - [Test Suite](docs/test_suite.md) diff --git a/docs/features.md b/docs/features.md new file mode 100644 index 0000000..2423d1b --- /dev/null +++ b/docs/features.md @@ -0,0 +1,113 @@ +# Toolchain Features + +These are the `cc_toolchain` features defined by the toolchain configs in +[`templates/linux/cc_toolchain_config.bzl.template`](../templates/linux/cc_toolchain_config.bzl.template) +and +[`templates/qnx/cc_toolchain_config.bzl.template`](../templates/qnx/cc_toolchain_config.bzl.template). + +Both toolchains enable **`no_legacy_features`**, which turns off the features +Bazel would otherwise add implicitly. As a result every flag the toolchain emits +comes from a feature defined explicitly below. + +Each entry is tagged with the platform(s) it applies to: **(both)**, **(Linux)**, +or **(QNX)**. Unless noted as *opt-in (disabled by default)*, a feature is +enabled by default. + +> **Note:** The order of features in the `features` list is significant — flags +> from a later feature appear after those from an earlier feature on the command +> line. + +## Capability markers +- **`no_legacy_features`** (both) — Disables Bazel's implicit legacy features. +- **`supports_pic`** (both) — Declares position-independent-code support. +- **`supports_dynamic_linker`** (both) — Declares dynamic-linking support. +- **`supports_fission`** (Linux) — Declares Fission (split DWARF) support. +- **`supports_header_path_normalization`** (Linux) — Suppresses absolute-path + warnings for system headers. +- **`dbg`** / **`opt`** (both) — Well-known build-mode markers used to select flags. + +## Compilation +- **`unfiltered_compile_flags`** (both) — Redacts `__DATE__`/`__TIME__`/`__TIMESTAMP__` + for reproducible builds. +- **`default_compile_flags`** (both) — Core compile flags plus `dbg`/`opt` + build-mode variants (exact flags differ per target). +- **`pic`** (Linux) — Emits `-fPIC` when the `pic` build variable is available. + (On QNX `-fPIC` is part of the default compile flags.) +- **`random_seed`** (both) — Emits `-frandom-seed=` for + deterministic output. +- **`include_paths`** (both) — Emits `-iquote` / `-I` / `-isystem` from the + `quote_include_paths`, `include_paths`, and `system_include_paths` variables. +- **`preprocessor_defines`** (both) — Emits `-D` defines (from `defines` / + `local_defines`). +- **`includes`** (Linux) — Emits `-include ` for force-included headers. +- **`user_compile_flags`** (both) — Passes through user-supplied compile flags + (`copts`). +- **`compiler_input_flags`** (both) — `-c` and the source file. +- **`compiler_output_flags`** (both) — `-S` / `-E` / `-o` output flags. +- **`compiler_library_search_paths`** (Linux) — Sets `LD_LIBRARY_PATH` so the + compiler binary finds its shared libraries. +- **`dependency_file`** (both) — Generates `.d` dependency files. On QNX, + **`dependency_file_named_implicitly`** toggles between explicit and implicit + naming. +- **`per_object_debug_info`** (Linux) — Emits `-gsplit-dwarf -g` under + `--fission` (guarded; no-op otherwise). + +## Linking / archiving +- **`archiver_flags`** (both) — `ar` flags (`rcsD`) and the object-file list for + static archives. +- **`user_link_flags`** (Linux) / **`user_link_flags_feature`** (QNX) — Passes + through user-supplied link flags (`linkopts`). +- **`linker_param_file`** (both) — Uses `@param_file` for linker/archiver args. +- **`default_link_flags`** (both) — Default/hardening link flags (exact flags + differ per target and build mode). +- **`library_search_directories`** (both) — Emits `-L` search paths. +- **`runtime_library_search_directories`** (both) — Emits `-Wl,-rpath` entries + (`$ORIGIN` for `cc_test`, `$EXEC_ORIGIN` otherwise). +- **`shared_flag`** (both) — `-shared` for dynamic libraries. +- **`output_execpath_flags`** (both) — `-o` for the link output. +- **`libraries_to_link`** (both) — Handles whole-archive, static, object-file, + dynamic, and versioned-dynamic library linking. +- **`sysroot_link_flags`** (Linux) — Adds `--sysroot` / `-Wl,--sysroot` at link. + +### Opt-in link features (Linux) +These mirror Bazel's legacy features and are guarded, so they are no-ops until +the relevant build mode is active. All are enabled by default except where noted. +- **`fission_support`** — `-Wl,--gdb-index` under `--fission`. +- **`linkstamps`** — Passes `%{linkstamp_paths}` to the linker (`--stamp`). +- **`force_pic_flags`** — `-pie` for executables under `--force_pic`. +- **`strip_debug_symbols`** — `-Wl,-S` when the link output is stripped + (e.g. `fastbuild`/`opt`, not `dbg`). +- **`static_libgcc`** — `-static-libgcc` when `static_link_cpp_runtimes` is on. +- **`fully_static_link`** — `-static` for fully static executables. **Opt-in + (disabled by default)**, since it forces static linking for every target and + requires static system archives (unavailable on some toolchains, e.g. AutoSD). + +## Warnings (all opt-in / disabled by default) +- **`minimal_warnings`** (both) — Baseline warning set. +- **`strict_warnings`** (both) — Stricter warnings; implies `minimal_warnings`. +- **`all_wall_warnings`** (Linux) — Broadest warning set; implies `strict_warnings`. +- **`warnings_as_errors`** (both) — Adds `-Werror`. + +## Sanitizers (Linux, opt-in) +- **`sanitizer`** — Base sanitizer feature. +- **`asan`** / **`lsan`** / **`tsan`** / **`ubsan`** — Address / Leak / Thread / + Undefined-Behavior sanitizers; each implies `sanitizer`. + +## Threading +- **`use_pthread`** (Linux) — Links with `-pthread`. + +## Extra flag hooks (template-substituted) +- **`extra_compile_flags`** (both) — Extra flags for all compile actions. +- **`extra_c_compile_flags`** / **`extra_cxx_compile_flags`** (Linux) — Extra C / + C++ compile flags. +- **`extra_link_flags`** (both) — Extra link flags. + +## Coverage +- **`coverage`** (both) — Base coverage feature. +- **`gcc_coverage_map_format`** (both) — `-fprofile-arcs -ftest-coverage` at + compile and `-lgcov` / `--coverage` at link. + +## QNX environment +- **`sdp_env`** (QNX) — Injects QNX SDP environment variables (`QNX_HOST`, + `QNX_TARGET`, license path, ...) into compile/link actions. +- **`qnx_license_env_info`** (QNX) — Adds an optional license environment entry. \ No newline at end of file diff --git a/docs/linux_toolchain_feature_set.md b/docs/linux_toolchain_feature_set.md deleted file mode 100644 index e179b15..0000000 --- a/docs/linux_toolchain_feature_set.md +++ /dev/null @@ -1,179 +0,0 @@ - - -# Linux Toolchain Required Feature Set - -## Purpose - -This document lists the feature set required for the Linux (GCC) C/C++ -toolchains generated from -[templates/linux/cc_toolchain_config.bzl.template](../templates/linux/cc_toolchain_config.bzl.template), -and classifies how each feature is currently provided. It is intended to drive -implementation, validation, and documentation follow-up when moving the Linux -toolchain to an explicit, `no_legacy_features`-based configuration. - -## How Features Are Resolved - -The Linux template **does not** enable the `no_legacy_features` feature. -As a result, Bazel automatically injects its full legacy feature set around the -features we declare. Every feature below is therefore in one of three states: - -| Classification | Meaning | -|---|---| -| **Explicit** | Defined in this template and present in the `features` list (or wired through an `action_config` / `tool_path`). | -| **Injected** | Not defined by us. Supplied automatically by Bazel's legacy feature injection because `no_legacy_features` is not enabled. | -| **Implicit** | Behavior provided without a dedicated feature — via `action_config` tool bindings, `builtin_sysroot`, `cxx_builtin_include_directories`, `tool_paths`, an `implies` reference, or flags baked into another explicit feature. | - -> Consequence: if `no_legacy_features` is enabled for Linux, every **Injected** -> feature marked *Required* below must be added explicitly, or the toolchain -> will stop compiling/linking. - -## Required Feature Set - -### Compilation - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `default_compile_flags` | Base compile flags (hardening, arch, build-mode variants) | Yes | Explicit | In `features` list. | -| `unfiltered_compile_flags` | Redact `__DATE__`/`__TIME__`/`__TIMESTAMP__` for reproducibility | Yes | Explicit | In `features` list. | -| `include_paths` | Emit `-I`, `-iquote`, `-isystem` | Yes | **Injected** | Legacy default. | -| `preprocessor_defines` | Emit `-D` defines | Yes | **Injected** | Legacy default. | -| `user_compile_flags` | Pass through per-target `copts` | Yes | **Injected** | Legacy default. Distinct from our `extra_*_compile_flags`. | -| `compiler_input_flags` | `-c` and source input | Yes | **Injected** | Legacy default. | -| `compiler_output_flags` | `-o` / `-S` / `-E` output | Yes | **Injected** | Legacy default. | -| `dependency_file` | Generate `.d` header deps (`-MD`) | Yes | **Injected** | Legacy default. Required for correct incremental builds. | -| `pic` | Emit `-fPIC` from the `pic` build variable | Yes | **Injected** | Legacy default. | -| `random_seed` | Deterministic `-frandom-seed` per output | Optional | Not provided | No feature; relies on compiler default. | - -### Linking - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `default_link_flags` | Base link flags (`-lm`, `-ldl`, `-lrt`, `-static-libstdc++`, `-static-libgcc`, and opt-only `-Wl,--gc-sections`) | Yes | Explicit | In `features` list. | -| `user_link_flags` | Pass through per-target `linkopts` | Yes | **Injected** | Legacy default. Distinct from our `extra_link_flags`. | -| `output_execpath_flags` | `-o` for link output | Yes | **Injected** | Legacy default. | -| `library_search_directories` | Emit `-L` search paths | Yes | **Injected** | Legacy default. | -| `runtime_library_search_directories` | Emit `-rpath` | Yes | **Injected** | Legacy default. | -| `libraries_to_link` | Emit `-l` / object linking, whole-archive, static/dynamic | Yes | **Injected** | Legacy default. | -| `shared_flag` | `-shared` for dynamic libraries | Yes | **Injected** | Legacy default. | -| `linker_param_file` | `@param_file` for long link commands | Yes | **Injected** | Legacy default. | -| `force_pic_flags` | `-pie` for position-independent executables | Optional | **Injected** | Legacy default. | -| `fully_static_link` | `-static` fully static linking | Optional | **Injected** | Legacy default; unused by current tests. | -| `static_libgcc` | `-static-libgcc` in opt dynamic links | Optional | **Injected** | Legacy default. | - -### Archiving - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `archiver_flags` | `ar` flags (`rcsD`) and library-list handling | Yes | **Injected** + Implicit | Feature injected; `cpp_link_static_library` `action_config` binds the `ar` tool and `implies = ["archiver_flags"]`. | - -### Sysroot - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `sysroot` (compile) | `--sysroot` on compile actions | Yes | Implicit | Provided via `builtin_sysroot` passed to `create_cc_toolchain_config_info`. | -| `sysroot_link_flags` | `--sysroot` / `-Wl,--sysroot` on link actions | Yes | Explicit | Custom feature in `features` list. | - -### Coverage - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `coverage` | Enable coverage instrumentation build mode | Yes | Explicit | In `features` list. | -| `gcc_coverage_map_format` | `-fprofile-arcs -ftest-coverage` / `--coverage` (gcov) | Yes | Explicit | In `features` list; `requires` `coverage`. | -| `llvm_coverage_map_format` | LLVM coverage map | No | **Injected** | Not used — GCC toolchain. | - -### Capabilities - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `supports_pic` | Declares PIC capability | Yes | Explicit | In `features` list. | -| `supports_dynamic_linker` | Declares dynamic-linker capability | Yes | Explicit | In `features` list. | -| `supports_fission` | Declares Fission capability | Optional | Explicit | In `features` list; no `per_object_debug_info`/`fission_support` features defined. | - -### Custom / project features - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `dbg` / `opt` | Build-mode selectors | Yes | Explicit | | -| `compiler_library_search_paths` | Set `LD_LIBRARY_PATH` for toolchain libs | Yes | Explicit | | -| `extra_compile_flags` / `extra_c_compile_flags` / `extra_cxx_compile_flags` | Injection point for user-supplied compile flags | Yes | Explicit | | -| `extra_link_flags` | Injection point for user-supplied link flags | Yes | Explicit | | -| `minimal_warnings` / `strict_warnings` / `all_wall_warnings` / `warnings_as_errors` | Warning-level control | Optional | Explicit | | -| `sanitizer` / `asan` / `lsan` / `tsan` / `ubsan` | Sanitizer builds | Optional | Explicit | | -| `use_pthread` | Link pthread | Optional | Explicit | | -| `gnu11` | Select `-std=gnu11` vs `-std=c11` | Optional | Explicit | Marked temporary in source. | -| `supports_header_path_normalization` | Suppress absolute-path warnings for system headers | Optional | Explicit | | - -### Tooling (non-feature wiring) - -| Behavior | Classification | Notes | -|---|---|---| -| `assemble` / `c_compile` / `cpp_compile` / link / `ar` / `strip` tool bindings | Implicit | Provided by `action_config` entries. | -| `gcov` path | Implicit | Provided by `tool_paths` (`gcov_wrapper`). | - -## Behaviors Depending On Bazel Legacy Defaults - -The following required behaviors are currently supplied **only** by Bazel's -legacy feature injection and are **not** defined in the Linux template. These -are the concrete gaps to close before enabling `no_legacy_features`: - -- `include_paths` -- `preprocessor_defines` -- `user_compile_flags` -- `compiler_input_flags` -- `compiler_output_flags` -- `dependency_file` -- `pic` (also partly implicit via `default_compile_flags`) -- `user_link_flags` -- `output_execpath_flags` -- `library_search_directories` -- `runtime_library_search_directories` -- `libraries_to_link` -- `shared_flag` -- `linker_param_file` -- `archiver_flags` (feature body; tool binding is already explicit via `action_config`) - -Optional legacy behaviors currently injected (add only if the capability is -needed): `force_pic_flags`, `fully_static_link`, `static_libgcc`, -`per_object_debug_info`, `fission_support`, `strip_debug_symbols`, `includes`, -`build_interface_libraries`, `dynamic_library_linker_tool`, `linkstamps`, and -all FDO/AutoFDO features. - -Not applicable to this toolchain: `llvm_coverage_map_format`, -`legacy_compile_flags`, `legacy_link_flags`. - -## Follow-Up Tasks - -**Implementation** -- Define the required Injected features explicitly in the Linux template, - mirroring the reference implementations already present in the QNX template - where applicable. -- Add the `no_legacy_features` feature (enabled) once the required set is - complete. -- Decide whether `pic` should be an explicit feature or remain baked into - `default_compile_flags`, and make it consistent with QNX. - -**Validation** -- Build and run the `tests/` suites (`feature_verification_tests`, - `language_and_standards_tests`) on the `x86_64-linux` config before and after - enabling `no_legacy_features`, and diff the resulting command lines - (`--subcommands`) to confirm parity. -- Verify static libraries (`archiver_flags`), shared libraries (`shared_flag`), - whole-archive linking (`libraries_to_link`), coverage, PIC, and header - dependency tracking (`dependency_file`) still function. - -**Documentation** -- Update [docs/features.md](features.md) to reflect the explicit feature set. -- Cross-link this document from [docs/overview.md](overview.md) and the README. diff --git a/docs/maintenance.md b/docs/maintenance.md index 69234a9..2a91f65 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -37,6 +37,42 @@ centralizes special sysroot flags in one place. Use explicit `gcc.sdp(...)` declarations when package metadata is local, experimental, or intentionally not part of the default support matrix. +## Toolchain Feature Wiring + +Both the Linux and QNX toolchain configs enable **`no_legacy_features`**, so +Bazel injects nothing implicitly — every compile/link/archive flag must come +from a feature (or wiring) declared in the template. See +[docs/features.md](features.md) for the full feature list. Not everything is a +feature, though; some behavior is provided by other toolchain wiring: + +- **Compiler / archiver / strip tools** are bound through `action_config` + entries (`assemble`, `c_compile`, `cpp_compile`, the link actions, and + `cpp_link_static_library`), not `tool_paths`. The static-library action + `implies = ["archiver_flags"]`, so the `archiver_flags` feature only supplies + the flags while the `action_config` supplies the `ar` binary. +- **`gcov`** is provided via `tool_paths` (`gcov_wrapper`). +- **Sysroot (Linux)** — the sysroot path is passed as `builtin_sysroot` to + `create_cc_toolchain_config_info`; compile-time header resolution relies on + `cxx_builtin_include_directories`, so no `--sysroot` is needed at compile. + Link-time `--sysroot` / `-Wl,--sysroot` is emitted by the custom + `sysroot_link_flags` feature. There is intentionally no legacy `sysroot` + compile feature. +- **Sysroot / system includes (QNX)** — QNX does not use `builtin_sysroot`; + system include roots come from `cxx_builtin_include_directories` (SDP paths), + and the SDP environment is injected by the `sdp_env` feature. + +When adding or auditing a feature, classify how each required behavior is +provided: an **explicit feature** in the `features` list, **tool/sysroot wiring** +as above, or **flags baked into another feature** (e.g. `-fPIC` is an explicit +`pic` feature on Linux but part of `default_compile_flags` on QNX). Under +`no_legacy_features` nothing is supplied automatically, so a missing behavior +means a broken build rather than a silent fallback. + +> **Static archives:** `fully_static_link` (`-static`) requires static system +> libraries (`libc.a`, `libstdc++.a`, ...). Toolchains that ship only shared +> libraries (e.g. AutoSD) cannot link fully static binaries, so that feature is +> opt-in and its test is marked incompatible with such platforms. + ## Common Gotchas - runtime-specific toolchains may need extra include and link flags that do not diff --git a/docs/qnx_toolchain_feature_set.md b/docs/qnx_toolchain_feature_set.md deleted file mode 100644 index 7bbe5d0..0000000 --- a/docs/qnx_toolchain_feature_set.md +++ /dev/null @@ -1,184 +0,0 @@ - - -# QNX Toolchain Required Feature Set - -## Purpose - -This document lists the feature set required for the QNX (QCC/SDP) C/C++ -toolchains generated from -[templates/qnx/cc_toolchain_config.bzl.template](../templates/qnx/cc_toolchain_config.bzl.template), -and classifies how each feature is currently provided. It is intended to drive -implementation, validation, and documentation follow-up when moving the QNX -toolchain to a fully explicit, `no_legacy_features`-based configuration. - -## How Features Are Resolved - -On `main`, the QNX template **does not** enable the `no_legacy_features` -feature, so Bazel still injects its legacy feature set around the features we -declare. The QNX template already declares several plumbing features explicitly -(more than Linux), but a number of required behaviors still come from legacy -injection. Every feature below is in one of three states: - -| Classification | Meaning | -|---|---| -| **Explicit** | Defined in this template and present in the `features` list (or wired through an `action_config` / `tool_path`). | -| **Injected** | Not defined by us. Supplied automatically by Bazel's legacy feature injection because `no_legacy_features` is not enabled. | -| **Implicit** | Behavior provided without a dedicated feature — via `action_config` tool bindings, `cxx_builtin_include_directories`, `tool_paths`, an `implies` reference, or flags baked into another explicit feature. | - -> Consequence: if `no_legacy_features` is enabled for QNX, every **Injected** -> feature marked *Required* below must be added explicitly, or the toolchain -> will stop compiling/linking. - -## Required Feature Set - -### Compilation - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `default_compile_flags` | Base compile flags (hardening, `-D_QNX_SOURCE`, arch, build-mode variants) | Yes | Explicit | In `features` list. | -| `unfiltered_compile_flags` | Redact `__DATE__`/`__TIME__`/`__TIMESTAMP__` for reproducibility | Yes | Explicit | In `features` list. | -| `sdp_env` | Inject `QNX_SDP_*` environment for compile/link | Yes | Explicit | QNX-specific; required for QCC to run. | -| `dependency_file` | Generate `.d` header deps (`-MD`) explicitly | Yes | Explicit | In `features` list. | -| `dependency_file_named_implicitly` | Alternate implicit dep-file naming mode | Yes | Explicit | In `features` list; toggles behavior of `dependency_file`. | -| `include_paths` | Emit `-I`, `-iquote`, `-isystem` | Yes | **Injected** | Legacy default. | -| `preprocessor_defines` | Emit `-D` defines | Yes | **Injected** | Legacy default. | -| `user_compile_flags` | Pass through per-target `copts` | Yes | **Injected** | Legacy default. Distinct from our `extra_compile_flags`. | -| `compiler_input_flags` | `-c` and source input | Yes | **Injected** | Legacy default. | -| `compiler_output_flags` | `-o` / `-S` / `-E` output | Yes | **Injected** | Legacy default. | -| `pic` | Emit `-fPIC` from the `pic` build variable | Yes | **Injected** | No explicit `pic` feature; validate whether `-fPIC` is baked into `default_compile_flags`. | -| `random_seed` | Deterministic `-frandom-seed` per output | Optional | Not provided | Not in the current `features` list. | - -### Linking - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `default_link_flags` | Hardening, `--as-needed`, libc++/libm | Yes | Explicit | In `features` list. | -| `runtime_library_search_directories` | Emit `-Wl,-rpath,$EXEC_ORIGIN/...` | Yes | Explicit | Custom rpath handling in `features` list. | -| `user_link_flags` | Pass through per-target `linkopts` | Yes | **Injected** | Legacy default. Distinct from our `extra_link_flags`. | -| `output_execpath_flags` | `-o` for link output | Yes | **Injected** | Legacy default. | -| `library_search_directories` | Emit `-L` search paths | Yes | **Injected** | Legacy default. | -| `libraries_to_link` | Emit `-l` / object linking, whole-archive, static/dynamic | Yes | **Injected** | Legacy default. | -| `shared_flag` | `-shared` for dynamic libraries | Yes | **Injected** | Legacy default. | -| `linker_param_file` | `@param_file` for long link commands | Yes | **Injected** | Legacy default. | -| `force_pic_flags` | `-pie` for position-independent executables | Optional | **Injected** | Legacy default. | - -### Archiving - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `archiver_flags` | `ar` flags and library-list handling | Yes | **Injected** + Implicit | Feature injected; `cpp_link_static_library` `action_config` binds the `ar` tool and `implies = ["archiver_flags"]`. | - -### Sysroot / system includes - -| Behavior | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `cxx_builtin_include_directories` | System include roots for QNX SDP | Yes | Implicit | Passed to `create_cc_toolchain_config_info`; QNX uses SDP paths rather than `builtin_sysroot`. | -| `sysroot` | `--sysroot` handling | N/A | **Injected** | QNX uses SDP environment (`sdp_env`) + builtin include dirs; validate whether the legacy `sysroot` feature has any effect. | - -### Coverage - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `coverage` | Enable coverage instrumentation build mode | Yes | Explicit | In `features` list. | -| `gcc_coverage_map_format` | `-fprofile-arcs -ftest-coverage` / `--coverage` (gcov) | Yes | Explicit | In `features` list; `requires` `coverage`. | -| `llvm_coverage_map_format` | LLVM coverage map | No | **Injected** | Not used — gcov-based. | - -### Capabilities - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `supports_pic` | Declares PIC capability | Yes | Explicit | In `features` list. | -| `supports_dynamic_linker` | Declares dynamic-linker capability | Yes | Explicit | In `features` list. | - -### Custom / QNX-specific features - -| Feature | Purpose | Required | Classification | Notes | -|---|---|---|---|---| -| `dbg` / `opt` | Build-mode selectors | Yes | Explicit | | -| `qnx_license_env_info` | Inject QNX license environment info | Yes | Explicit | `qnx_license_env_info` feature. | -| `extra_compile_flags` / `extra_link_flags` | Injection point for user-supplied flags | Yes | Explicit | | -| `minimal_warnings` / `strict_warnings` / `all_wall_warnings` / `warnings_as_errors` | Warning-level control | Optional | Explicit | | - -### Tooling (non-feature wiring) - -| Behavior | Classification | Notes | -|---|---|---| -| `assemble` / `c_compile` / `cpp_compile` / link / `ar` / `strip` tool bindings | Implicit | Provided by `action_config` entries (QCC / `ar` / `strip`). | -| `gcov` path | Implicit | Provided by `tool_paths` (`gcov_wrapper`). | - -## Behaviors Depending On Bazel Legacy Defaults - -The following required behaviors are currently supplied **only** by Bazel's -legacy feature injection and are **not** defined in the QNX template. These are -the concrete gaps to close before enabling `no_legacy_features`: - -- `include_paths` -- `preprocessor_defines` -- `user_compile_flags` -- `compiler_input_flags` -- `compiler_output_flags` -- `pic` (validate against `default_compile_flags`) -- `user_link_flags` -- `output_execpath_flags` -- `library_search_directories` -- `libraries_to_link` -- `shared_flag` -- `linker_param_file` -- `archiver_flags` (feature body; tool binding is already explicit via `action_config`) - -Already handled explicitly by QNX (no gap): `default_compile_flags`, -`unfiltered_compile_flags`, `dependency_file`, `dependency_file_named_implicitly`, -`runtime_library_search_directories`, `coverage`, `gcc_coverage_map_format`, -`default_link_flags`, `sdp_env`, `qnx_license_env_info`. - -Optional legacy behaviors currently injected (add only if the capability is -needed): `force_pic_flags`, `fully_static_link`, `static_libgcc`, -`per_object_debug_info`, `fission_support`, `strip_debug_symbols`, `includes`, -`build_interface_libraries`, `dynamic_library_linker_tool`, `linkstamps`, and -all FDO/AutoFDO features. - -Not applicable to this toolchain: `llvm_coverage_map_format`, -`legacy_compile_flags`, `legacy_link_flags`. - -## Follow-Up Tasks - -**Implementation** -- Define the required Injected features explicitly in the QNX template - (`include_paths`, `preprocessor_defines`, `user_compile_flags`, - `compiler_input_flags`, `compiler_output_flags`, `user_link_flags`, - `output_execpath_flags`, `library_search_directories`, `libraries_to_link`, - `shared_flag`, `linker_param_file`, `archiver_flags`, `pic`). -- Add the `no_legacy_features` feature (enabled) once the required set is - complete. -- Confirm `-fPIC` handling: either add an explicit `pic` feature or keep it in - `default_compile_flags`, consistently with Linux. - -**Validation** -- Build the `tests/` suites with the `x86_64-qnx` and `aarch64-qnx` configs and - diff command lines (`--subcommands`) before and after enabling - `no_legacy_features` to confirm parity. -- Because QNX targets are cross-compiled, execute the tests on the matching QNX - target platform (see [docs/test_suite.md](test_suite.md)); on the host, - restrict validation to `bazel build`. -- Verify static/shared library creation, whole-archive linking, coverage, - dependency files, licensing (`qnx_license_env_info`), and SDP environment - wiring (`sdp_env`). - -**Documentation** -- Update [docs/features.md](features.md) and - [docs/qnx_integration.md](qnx_integration.md) to reflect the explicit feature - set. -- Cross-link this document from [docs/overview.md](overview.md) and the README. From 626cd098a5a20443d923817547cafc97be7c32eb Mon Sep 17 00:00:00 2001 From: Nikola Radakovic Date: Tue, 14 Jul 2026 17:27:04 +0200 Subject: [PATCH 3/5] Add feature-verification tests Add tests that genuinely exercise the features implemented under no_legacy_features: - preprocessor_defines_test: defines via the local_defines attribute (distinct from copts/user_compile_flags) - include_dir_test: include_paths via a library's `includes` attribute - user_link_flags_test: linkopts forwarded through user_link_flags (--defsym) - random_seed_test: reproducible-build random seed smoke test - fully_static_link_test: verifies a fully static binary maps no shared objects; marked incompatible with toolchains lacking static archives (AutoSD) Add an opt_in_features/ package with manual tests for the disabled-by-default or build-mode-gated features (per_object_debug_info, fission_support, linkstamps, includes, force_pic_flags, strip_debug_symbols, static_libgcc), and a genuine force_pic PIE check run via a dedicated --force_pic CI step. Wire the new tests into //:feature_verification_tests and update the x86_64-linux workflow. Refresh docs/test_suite.md and docs/tests_and_validation.md to list the new tests, the opt_in_features package, and the directory layout. resolves #78 --- .github/workflows/x86_64-linux.yml | 3 + docs/test_suite.md | 40 +++++- docs/tests_and_validation.md | 7 +- tests/BUILD | 5 + tests/feature_verification/BUILD | 70 ++++++++++ .../fully_static_link_test.cpp | 55 ++++++++ .../include_dir/custom_math.h | 27 ++++ .../feature_verification/include_dir_test.cpp | 35 +++++ .../opt_in_features/BUILD | 127 ++++++++++++++++++ .../opt_in_features/force_pic_test.cpp | 72 ++++++++++ .../opt_in_features/opt_in_smoke.cpp | 29 ++++ .../preprocessor_defines_test.cpp | 46 +++++++ .../feature_verification/random_seed_test.cpp | 52 +++++++ .../user_link_flags_test.cpp | 42 ++++++ 14 files changed, 607 insertions(+), 3 deletions(-) create mode 100644 tests/feature_verification/fully_static_link_test.cpp create mode 100644 tests/feature_verification/include_dir/custom_math.h create mode 100644 tests/feature_verification/include_dir_test.cpp create mode 100644 tests/feature_verification/opt_in_features/BUILD create mode 100644 tests/feature_verification/opt_in_features/force_pic_test.cpp create mode 100644 tests/feature_verification/opt_in_features/opt_in_smoke.cpp create mode 100644 tests/feature_verification/preprocessor_defines_test.cpp create mode 100644 tests/feature_verification/random_seed_test.cpp create mode 100644 tests/feature_verification/user_link_flags_test.cpp diff --git a/.github/workflows/x86_64-linux.yml b/.github/workflows/x86_64-linux.yml index 3341605..aa466f9 100644 --- a/.github/workflows/x86_64-linux.yml +++ b/.github/workflows/x86_64-linux.yml @@ -42,3 +42,6 @@ jobs: - name: Bazel Test run: | bazel test --config=x86_64-linux -- //:language_and_standards_tests //:feature_verification_tests + - name: Bazel Test (force_pic) + run: | + bazel test --config=x86_64-linux --force_pic -- //feature_verification/opt_in_features:force_pic_flags_test diff --git a/docs/test_suite.md b/docs/test_suite.md index 266b5e3..c815f29 100644 --- a/docs/test_suite.md +++ b/docs/test_suite.md @@ -71,6 +71,35 @@ These tests verify that specific toolchain features are correctly implemented an - Verifies `-Wl,--whole-archive` and `-Wl,--no-whole-archive` work - Tests unused library symbols are linked when using whole-archive +9. **`preprocessor_defines_test`** - `preprocessor_defines` feature + - Injects defines via the `local_defines` attribute (routed through the + `preprocessor_defines` feature), unlike `defines_test` which uses `copts` + (routed through `user_compile_flags`) + +10. **`include_dir_test`** - `include_paths` via the `includes` attribute + - Includes a header exposed through a library's `includes` attribute, + exercising the `-I`/`-isystem` search paths + +11. **`user_link_flags_test`** - `user_link_flags` feature + - Passes a linker flag through `linkopts` (`-Wl,--defsym=...`) and checks the + injected symbol's address at runtime + +12. **`random_seed_test`** - `random_seed` feature + - Build-and-run smoke test exercising internal-linkage symbols governed by + the reproducible-build random seed + +13. **`fully_static_link_test`** - `fully_static_link` feature (`-static`) + - Verifies no shared objects are mapped at runtime (fully static binary) + - Marked incompatible with toolchains lacking static system archives (AutoSD) + +> **Opt-in feature tests** (`tests/feature_verification/opt_in_features/`) cover +> features that are disabled by default or only emit flags under a specific build +> mode (`per_object_debug_info`, `fission_support`, `linkstamps`, `includes`, +> `force_pic_flags`, `strip_debug_symbols`, `static_libgcc`). They are tagged +> `manual` and excluded from CI (except `force_pic_flags_test`, run in a +> dedicated `--force_pic` CI step). See that package's BUILD file for the exact +> command to exercise each one. + #### Run Feature Tests ```bash @@ -282,7 +311,12 @@ tests/ │ ├── BUILD # Feature test definitions │ ├── feature_test.h/cpp # Test infrastructure │ ├── defines_test.cpp # Individual feature tests +│ ├── preprocessor_defines_test.cpp │ ├── include_paths_test.cpp +│ ├── include_dir/custom_math.h and include_dir_test.cpp +│ ├── user_link_flags_test.cpp +│ ├── random_seed_test.cpp +│ ├── fully_static_link_test.cpp │ ├── warnings_test.cpp │ ├── coverage_test.cpp │ ├── pic_test_lib.h/cpp, pic_test.cpp @@ -291,7 +325,11 @@ tests/ │ ├── whole_archive_lib.h/cpp │ ├── whole_archive_test.cpp │ ├── pthread_test_lib.h/cpp -│ └── pthread_test.cpp +│ ├── pthread_test.cpp +│ └── opt_in_features/ # Manual, disabled-by-default features +│ ├── BUILD +│ ├── opt_in_smoke.cpp +│ └── force_pic_test.cpp └── language_and_standards/ ├── BUILD # Language test definitions ├── c_lang_test.c # C language tests diff --git a/docs/tests_and_validation.md b/docs/tests_and_validation.md index 957d2aa..eaaecc2 100644 --- a/docs/tests_and_validation.md +++ b/docs/tests_and_validation.md @@ -53,8 +53,11 @@ test suites that aggregate the individual test targets. `tests/feature_verification/` C++ targets that verify toolchain features such as preprocessor defines, -include paths, warnings, coverage, position-independent code, pthread support, -multi-file archiving, and whole-archive linking. +include paths (including the `includes` attribute), user-supplied compile and +link flags, warnings, coverage, position-independent code, pthread support, +multi-file archiving, whole-archive linking, reproducible-build random seed, and +fully static linking. The `opt_in_features/` subpackage holds manual tests for +features that are disabled by default or require a specific build mode. `tests/language_and_standards/` diff --git a/tests/BUILD b/tests/BUILD index e45fce2..0274cfa 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -40,10 +40,15 @@ test_suite( tests = [ "//feature_verification:coverage_test", "//feature_verification:defines_test", + "//feature_verification:fully_static_link_test", + "//feature_verification:include_dir_test", "//feature_verification:include_paths_test", "//feature_verification:multifile_test", "//feature_verification:pic_test", + "//feature_verification:preprocessor_defines_test", "//feature_verification:pthread_test", + "//feature_verification:random_seed_test", + "//feature_verification:user_link_flags_test", "//feature_verification:whole_archive_test", ], ) diff --git a/tests/feature_verification/BUILD b/tests/feature_verification/BUILD index b52d2ba..73d7cb9 100644 --- a/tests/feature_verification/BUILD +++ b/tests/feature_verification/BUILD @@ -61,6 +61,76 @@ cc_test( deps = [":feature_test_lib"], ) +# Test for: preprocessor_defines feature +# Unlike defines_test (which uses copts -> user_compile_flags), this target +# injects defines through the `local_defines` attribute, which is forwarded to +# the compiler only by the preprocessor_defines feature. +cc_test( + name = "preprocessor_defines_test", + srcs = ["preprocessor_defines_test.cpp"], + local_defines = [ + "PP_DEFINES_FEATURE_ENABLED=1", + "PP_DEFINES_ANSWER=42", + "PP_DEFINES_LABEL=\\\"from_local_defines\\\"", + ], +) + +# Library that exposes its header directory via the `includes` attribute. +# Consumers include "custom_math.h" without the directory prefix, which only +# resolves when the include_paths feature emits the -I/-isystem search path. +cc_library( + name = "custom_math", + hdrs = ["include_dir/custom_math.h"], + includes = ["include_dir"], + linkstatic = select({ + "@score_bazel_platforms//runtime_es:ebclfsa": True, + "//conditions:default": False, + }), +) + +# Test for: include_paths feature (-I / -isystem via the `includes` attribute) +cc_test( + name = "include_dir_test", + srcs = ["include_dir_test.cpp"], + deps = [":custom_math"], +) + +# Test for: user_link_flags feature +# Passes a linker flag through `linkopts`; Bazel forwards linkopts to the linker +# only via the user_link_flags feature. The --defsym flag defines an absolute +# symbol whose address is checked at runtime. +cc_test( + name = "user_link_flags_test", + srcs = ["user_link_flags_test.cpp"], + linkopts = ["-Wl,--defsym=user_link_flags_marker=0x1234"], +) + +# Test for: fully_static_link feature (-static) +# Enabling the feature emits -static directly, producing a binary with no +# dynamic library dependencies. The test verifies no shared objects are mapped +# at runtime. This feature is opt-in, so the target enables it explicitly. +# +# Fully static linking requires static system archives (libc.a, libstdc++.a, +# ...). The AutoSD toolchain ships only shared libraries, so the test is marked +# incompatible with the autosd10 platform and is skipped there. +cc_test( + name = "fully_static_link_test", + srcs = ["fully_static_link_test.cpp"], + features = ["fully_static_link"], + target_compatible_with = select({ + "@score_bazel_platforms//runtime_es:autosd10": ["@platforms//:incompatible"], + "//conditions:default": [], + }), +) + +# Test for: random_seed feature (-frandom-seed=) +# Build-and-run smoke test exercising internal-linkage symbols governed by the +# random seed used for reproducible builds. +cc_test( + name = "random_seed_test", + srcs = ["random_seed_test.cpp"], +) + # Test for: strict_warnings, minimal_warnings, warnings_as_errors features # Verifies that warning levels are correctly configured cc_test( diff --git a/tests/feature_verification/fully_static_link_test.cpp b/tests/feature_verification/fully_static_link_test.cpp new file mode 100644 index 0000000..332e933 --- /dev/null +++ b/tests/feature_verification/fully_static_link_test.cpp @@ -0,0 +1,55 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test for: fully_static_link feature (-static) +// +// The fully_static_link feature emits "-static" on the link, producing an +// executable with no dynamic library dependencies. This test confirms the +// binary is genuinely statically linked by scanning its own memory map +// (/proc/self/maps): a fully static binary maps no shared objects, so no +// mapped file path ends in ".so" (or contains ".so."). + +#include +#include +#include +#include + +namespace { + +// Returns true if the running process has any shared object (.so) mapped, +// which would indicate the binary was NOT fully statically linked. +bool has_shared_object_mappings() { + std::ifstream maps("/proc/self/maps"); + assert(maps.is_open() && "could not open /proc/self/maps"); + + std::string line; + while (std::getline(maps, line)) { + if (line.find(".so") != std::string::npos) { + return true; + } + } + return false; +} + +} // namespace + +int main() { + std::cout << "Testing fully_static_link feature (-static)..." << std::endl; + + assert(!has_shared_object_mappings() && + "expected a fully static binary (no .so mappings)"); + + std::cout << "fully_static_link test passed: no shared objects mapped" + << std::endl; + return 0; +} diff --git a/tests/feature_verification/include_dir/custom_math.h b/tests/feature_verification/include_dir/custom_math.h new file mode 100644 index 0000000..a1b16c6 --- /dev/null +++ b/tests/feature_verification/include_dir/custom_math.h @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef INCLUDE_DIR_CUSTOM_MATH_H +#define INCLUDE_DIR_CUSTOM_MATH_H + +// This header lives in an `includes`-exposed directory. Consumers include it as +// "custom_math.h" (without the directory prefix), which only resolves when the +// include_paths feature emits the -I/-isystem search path for that directory. +namespace custom_math { + +inline int triple(int value) { + return value * 3; +} + +} // namespace custom_math + +#endif // INCLUDE_DIR_CUSTOM_MATH_H diff --git a/tests/feature_verification/include_dir_test.cpp b/tests/feature_verification/include_dir_test.cpp new file mode 100644 index 0000000..29f3080 --- /dev/null +++ b/tests/feature_verification/include_dir_test.cpp @@ -0,0 +1,35 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test for: include_paths feature (-I / -isystem via the `includes` attribute) +// +// The custom_math library exposes its header directory through the `includes` +// attribute. Bazel translates that into an include search path that is only +// passed to the compiler by the `include_paths` toolchain feature. Because the +// header is included below without its directory prefix, the compile only +// succeeds when that search path is emitted. + +#include "custom_math.h" + +#include +#include + +int main() { + std::cout << "Testing include_paths feature (includes attribute)..." << std::endl; + + assert(custom_math::triple(7) == 21); + + std::cout << "custom_math::triple(7) = " << custom_math::triple(7) << std::endl; + std::cout << "All include_paths (includes attribute) tests passed!" << std::endl; + return 0; +} diff --git a/tests/feature_verification/opt_in_features/BUILD b/tests/feature_verification/opt_in_features/BUILD new file mode 100644 index 0000000..f247cd6 --- /dev/null +++ b/tests/feature_verification/opt_in_features/BUILD @@ -0,0 +1,127 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +"""Opt-in (disabled-by-default) toolchain feature tests. + +Each target below exercises one of the opt-in features added to the Linux +toolchain. The features are disabled by default, so every target enables its +feature explicitly via the `features` attribute. Some features only emit flags +when an additional build mode is active (e.g. fission or force-pic); the exact +command needed to make each feature take effect is documented on the target. + +All targets are tagged "manual" so that: + * CI wildcard runs (`bazel test //...`) skip them, and + * they are NOT part of the `//:feature_verification_tests` suite. + +Run them explicitly on demand, e.g.: + bazel test --config x86_64-linux \\ + //feature_verification/opt_in_features:fully_static_link_test +""" + +load("@rules_cc//cc:defs.bzl", "cc_test") + +# --------------------------------------------------------------------------- +# Compile-side opt-in features +# --------------------------------------------------------------------------- + +# per_object_debug_info -> "-gsplit-dwarf -g" (fission split DWARF). +# Only emitted when fission is active; run with: +# bazel test --config x86_64-linux --fission=yes \ +# //feature_verification/opt_in_features:per_object_debug_info_test +cc_test( + name = "per_object_debug_info_test", + srcs = ["opt_in_smoke.cpp"], + features = ["per_object_debug_info"], + tags = ["manual"], +) + +# includes -> "-include %{includes}" (force-included headers). +# The `includes` build variable is only populated for force-included headers. +cc_test( + name = "includes_test", + srcs = ["opt_in_smoke.cpp"], + features = ["includes"], + tags = ["manual"], +) + +# --------------------------------------------------------------------------- +# Link-side opt-in features +# --------------------------------------------------------------------------- + +# fission_support -> "-Wl,--gdb-index". Only emitted when fission is active: +# bazel test --config x86_64-linux --fission=yes \ +# //feature_verification/opt_in_features:fission_support_test +cc_test( + name = "fission_support_test", + srcs = ["opt_in_smoke.cpp"], + features = ["fission_support"], + tags = ["manual"], +) + +# linkstamps -> passes "%{linkstamp_paths}" to the linker. +# Requires linkstamp inputs and --stamp to populate linkstamp_paths. +cc_test( + name = "linkstamps_test", + srcs = ["opt_in_smoke.cpp"], + features = ["linkstamps"], + tags = ["manual"], +) + +# force_pic_flags -> "-pie" for executables. Only emitted under --force_pic. The +# test asserts its own ELF type is ET_DYN (PIE), so it must be run with: +# bazel test --config x86_64-linux --force_pic \ +# //feature_verification/opt_in_features:force_pic_flags_test +cc_test( + name = "force_pic_flags_test", + srcs = ["force_pic_test.cpp"], + features = ["force_pic_flags"], + tags = ["manual"], +) + +# strip_debug_symbols -> "-Wl,-S". Emitted when debug-symbol stripping is +# requested for the link output. +cc_test( + name = "strip_debug_symbols_test", + srcs = ["opt_in_smoke.cpp"], + features = ["strip_debug_symbols"], + tags = ["manual"], +) + +# static_libgcc -> "-static-libgcc" (gated on the static_link_cpp_runtimes +# feature, which must also be enabled). +cc_test( + name = "static_libgcc_test", + srcs = ["opt_in_smoke.cpp"], + features = [ + "static_libgcc", + "static_link_cpp_runtimes", + ], + tags = ["manual"], +) + +# --------------------------------------------------------------------------- +# Convenience suite (also manual; not wired into CI). +# --------------------------------------------------------------------------- +test_suite( + name = "opt_in_feature_tests", + tags = ["manual"], + tests = [ + ":fission_support_test", + ":force_pic_flags_test", + ":includes_test", + ":linkstamps_test", + ":per_object_debug_info_test", + ":static_libgcc_test", + ":strip_debug_symbols_test", + ], +) diff --git a/tests/feature_verification/opt_in_features/force_pic_test.cpp b/tests/feature_verification/opt_in_features/force_pic_test.cpp new file mode 100644 index 0000000..dd7bcb1 --- /dev/null +++ b/tests/feature_verification/opt_in_features/force_pic_test.cpp @@ -0,0 +1,72 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test for: force_pic_flags feature (-pie under --force_pic) +// +// The force_pic_flags feature emits "-pie" for executable links when the +// `force_pic` build variable is available, which happens under --force_pic. +// A position-independent executable is an ELF object of type ET_DYN (as opposed +// to a classic ET_EXEC). This test reads its own ELF header via /proc/self/exe +// and asserts the type is ET_DYN, i.e. the binary really was linked as a PIE. +// +// This target is exercised in CI with: +// bazel test --config x86_64-linux --force_pic \ +// //feature_verification/opt_in_features:force_pic_flags_test + +#include +#include +#include +#include + +namespace { + +constexpr std::uint16_t kElfTypeExecutable = 2; // ET_EXEC (non-PIE) +constexpr std::uint16_t kElfTypeSharedObject = 3; // ET_DYN (PIE / shared) + +// Reads the e_type field from this process's own ELF header. +std::uint16_t read_own_elf_type() { + std::FILE* self = std::fopen("/proc/self/exe", "rb"); + assert(self != nullptr && "could not open /proc/self/exe"); + + unsigned char header[20] = {0}; + const std::size_t read = std::fread(header, 1, sizeof(header), self); + std::fclose(self); + assert(read >= 18 && "short read of ELF header"); + + // ELF magic: 0x7F 'E' 'L' 'F'. + assert(header[0] == 0x7F && header[1] == 'E' && header[2] == 'L' && + header[3] == 'F' && "not an ELF file"); + + // e_type is a 16-bit field at offset 16 (little-endian on x86_64). + return static_cast(header[16]) | + static_cast(header[17] << 8); +} + +} // namespace + +int main() { + std::cout << "Testing force_pic_flags feature (PIE executable)..." << std::endl; + + const std::uint16_t e_type = read_own_elf_type(); + std::cout << "ELF e_type = " << e_type + << " (2=ET_EXEC, 3=ET_DYN/PIE)" << std::endl; + + // With --force_pic the executable must be a position-independent ET_DYN. + assert(e_type == kElfTypeSharedObject && + "expected a PIE executable (ET_DYN); was force_pic applied?"); + (void)kElfTypeExecutable; + + std::cout << "force_pic_flags test passed: executable is PIE (ET_DYN)" + << std::endl; + return 0; +} diff --git a/tests/feature_verification/opt_in_features/opt_in_smoke.cpp b/tests/feature_verification/opt_in_features/opt_in_smoke.cpp new file mode 100644 index 0000000..2876268 --- /dev/null +++ b/tests/feature_verification/opt_in_features/opt_in_smoke.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Shared entry point for the opt-in (disabled-by-default) toolchain feature +// tests. Each cc_test target in this package enables exactly one opt-in feature +// through its `features` attribute (and, where required, an additional command +// line flag documented in the BUILD file). These targets are tagged "manual" so +// they are excluded from CI wildcard runs; they exist so the opt-in features can +// be exercised and manually verified on demand. +// +// The body is a minimal build-and-run smoke test: it confirms that a binary +// built with the feature enabled compiles, links, and runs successfully. + +#include + +int main() { + std::cout << "opt-in feature smoke test: build + link + run OK" << std::endl; + return 0; +} diff --git a/tests/feature_verification/preprocessor_defines_test.cpp b/tests/feature_verification/preprocessor_defines_test.cpp new file mode 100644 index 0000000..4d00cc9 --- /dev/null +++ b/tests/feature_verification/preprocessor_defines_test.cpp @@ -0,0 +1,46 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test for: preprocessor_defines feature +// +// Unlike defines_test (which passes -D via copts and therefore exercises the +// user_compile_flags feature), this test relies on the `local_defines` target +// attribute. Bazel forwards those values to the compiler exclusively through +// the `preprocessor_defines` toolchain feature. If that feature is not applied, +// the -D flags are never emitted and the #error directives below fail the build. + +#ifndef PP_DEFINES_FEATURE_ENABLED +#error "preprocessor_defines feature not applied: PP_DEFINES_FEATURE_ENABLED is undefined" +#endif + +#ifndef PP_DEFINES_ANSWER +#error "preprocessor_defines feature not applied: PP_DEFINES_ANSWER is undefined" +#endif + +#include +#include +#include + +int main() { + std::cout << "Testing preprocessor_defines feature..." << std::endl; + + // Values injected through the `local_defines` attribute. + assert(PP_DEFINES_FEATURE_ENABLED == 1); + assert(PP_DEFINES_ANSWER == 42); + assert(std::strcmp(PP_DEFINES_LABEL, "from_local_defines") == 0); + + std::cout << "PP_DEFINES_ANSWER = " << PP_DEFINES_ANSWER << std::endl; + std::cout << "PP_DEFINES_LABEL = " << PP_DEFINES_LABEL << std::endl; + std::cout << "All preprocessor_defines tests passed!" << std::endl; + return 0; +} diff --git a/tests/feature_verification/random_seed_test.cpp b/tests/feature_verification/random_seed_test.cpp new file mode 100644 index 0000000..004b803 --- /dev/null +++ b/tests/feature_verification/random_seed_test.cpp @@ -0,0 +1,52 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test for: random_seed feature (-frandom-seed=) +// +// The random_seed feature makes builds reproducible by deriving GCC's internal +// random seed from the output file path. That seed is used when generating the +// mangled names of internal-linkage symbols (anonymous namespaces, local +// statics, lambdas). Its effect is a build-reproducibility property and is not +// directly observable at runtime within a single binary, so this is a +// build-and-run smoke test: it exercises exactly the kind of symbols the seed +// governs and confirms the toolchain compiles, links, and runs them correctly. + +#include +#include + +namespace { + +int hidden_counter() { + static int value = 0; + return ++value; +} + +struct HiddenHelper { + int scale(int x) const { return x * 2; } +}; + +} // namespace + +int main() { + std::cout << "Testing random_seed feature (build reproducibility)..." << std::endl; + + assert(hidden_counter() == 1); + assert(hidden_counter() == 2); + + HiddenHelper helper; + auto doubler = [&helper](int x) { return helper.scale(x); }; + assert(doubler(21) == 42); + + std::cout << "All random_seed tests passed!" << std::endl; + return 0; +} diff --git a/tests/feature_verification/user_link_flags_test.cpp b/tests/feature_verification/user_link_flags_test.cpp new file mode 100644 index 0000000..296097a --- /dev/null +++ b/tests/feature_verification/user_link_flags_test.cpp @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test for: user_link_flags feature +// +// The target passes a linker flag through the `linkopts` attribute: +// -Wl,--defsym=user_link_flags_marker=0x1234 +// Bazel forwards `linkopts` to the linker exclusively via the `user_link_flags` +// toolchain feature. The linker's --defsym option defines the absolute symbol +// `user_link_flags_marker` at address 0x1234. If the user_link_flags feature is +// not applied, the flag is dropped, the symbol stays undefined, and linking +// fails. When the feature works, the symbol's address equals the injected value. + +#include +#include +#include + +extern "C" { +extern char user_link_flags_marker; +} + +int main() { + std::cout << "Testing user_link_flags feature..." << std::endl; + + auto marker_address = reinterpret_cast(&user_link_flags_marker); + assert(marker_address == 0x1234); + + std::cout << "user_link_flags_marker address = 0x" << std::hex << marker_address + << std::dec << std::endl; + std::cout << "All user_link_flags tests passed!" << std::endl; + return 0; +} From 550963bd001cb46a112a98bfa75250ad2734fc1a Mon Sep 17 00:00:00 2001 From: Nikola Radakovic Date: Wed, 15 Jul 2026 10:07:48 +0100 Subject: [PATCH 4/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nikola Radakovic --- templates/qnx/cc_toolchain_config.bzl.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/qnx/cc_toolchain_config.bzl.template b/templates/qnx/cc_toolchain_config.bzl.template index 2c89b31..054c61b 100644 --- a/templates/qnx/cc_toolchain_config.bzl.template +++ b/templates/qnx/cc_toolchain_config.bzl.template @@ -337,7 +337,7 @@ def _impl(ctx): # Core linking features user_link_flags_feature = feature( - name = "user_link_flags_feature", + name = "user_link_flags", enabled = True, flag_sets = [ flag_set( From 5f264a6e0f1aa0d65af0a45b5add763f55516541 Mon Sep 17 00:00:00 2001 From: Nikola Radakovic Date: Wed, 15 Jul 2026 10:08:02 +0100 Subject: [PATCH 5/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nikola Radakovic --- docs/features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features.md b/docs/features.md index 2423d1b..84a3dfe 100644 --- a/docs/features.md +++ b/docs/features.md @@ -55,7 +55,7 @@ enabled by default. ## Linking / archiving - **`archiver_flags`** (both) — `ar` flags (`rcsD`) and the object-file list for static archives. -- **`user_link_flags`** (Linux) / **`user_link_flags_feature`** (QNX) — Passes +- **`user_link_flags`** (both) — Passes through user-supplied link flags (`linkopts`). - **`linker_param_file`** (both) — Uses `@param_file` for linker/archiver args. - **`default_link_flags`** (both) — Default/hardening link flags (exact flags