From 53f174c999aeef741e9aeca51961d8520a339210 Mon Sep 17 00:00:00 2001 From: Nathaniel Brough Date: Wed, 1 Mar 2023 17:37:22 -0800 Subject: [PATCH] feat: Adds build variable expansion --- MODULE.bazel | 1 + WORKSPACE | 13 ++ cc/BUILD.bazel | 15 +- cc/common_features/BUILD.bazel | 36 +++++ cc/features.bzl | 135 ++++++++++++++++-- cc/toolchain.bzl | 77 ++++++++++ .../clang_llvm_x86_64_linux_gnu_ubuntu.BUILD | 40 ++++++ 7 files changed, 305 insertions(+), 12 deletions(-) create mode 100644 cc/common_features/BUILD.bazel create mode 100644 cc/toolchain.bzl diff --git a/MODULE.bazel b/MODULE.bazel index dbdf6de..aa299c9 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -4,4 +4,5 @@ module( ) bazel_dep(name = "rules_cc", version = "0.0.4") +bazel_dep(name = "bazel_skylib", version = "1.4.1") bazel_dep(name = "stardoc", version = "0.5.3") diff --git a/WORKSPACE b/WORKSPACE index e2fe5d8..0518d6c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,6 +4,19 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") load("//third_party:test_repos.bzl", "test_repos") +http_archive( + name = "bazel_skylib", + sha256 = "b8a1527901774180afc798aeb28c4634bdccf19c4d98e7bdd1ce79d1fe9aaad7", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.1/bazel-skylib-1.4.1.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.1/bazel-skylib-1.4.1.tar.gz", + ], +) + +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") + +bazel_skylib_workspace() + http_archive( name = "io_bazel_stardoc", sha256 = "3fd8fec4ddec3c670bd810904e2e33170bedfe12f90adf943508184be458c8bb", diff --git a/cc/BUILD.bazel b/cc/BUILD.bazel index 50d1ecc..89dcefd 100644 --- a/cc/BUILD.bazel +++ b/cc/BUILD.bazel @@ -2,6 +2,7 @@ load("@modular_cc_toolchain//cc:features.bzl", "cc_feature", "action_mux") load("//cc:action_names.bzl", "ACTION_NAME_GROUPS") load("//cc:actions.bzl", "cc_action_config") load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") +load("//cc:toolchain.bzl", "cc_toolchain_config") cc_feature( name = "garbage_collect_sections", @@ -18,14 +19,24 @@ cc_feature( cc_action_config( name = "default_clang", action_tools = { - ACTION_NAMES.c_compile: "@clang_llvm_x86_64_linux_gnu_ubuntu//:bin/clang", + ACTION_NAMES.c_compile: "@clang_llvm_x86_64_linux_gnu_ubuntu//:cc", } ) +cc_toolchain_config( + name = "test_toolchain_config", + cc_features = [ + ":garbage_collect_sections", + ], + action_configs = [ + ":default_clang", + ] +) + filegroup( name = "bzl_srcs", srcs = glob(["*.bzl"]), visibility = ["//visibility:public"], ) -exports_files(glob(["*.bzl"])) \ No newline at end of file +exports_files(glob(["*.bzl"])) diff --git a/cc/common_features/BUILD.bazel b/cc/common_features/BUILD.bazel new file mode 100644 index 0000000..3ce2339 --- /dev/null +++ b/cc/common_features/BUILD.bazel @@ -0,0 +1,36 @@ +load("@modular_cc_toolchain//cc:features.bzl", "cc_feature", "action_mux") +load("//cc:action_names.bzl", "ACTION_NAME_GROUPS") +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") + +cc_feature( + name = "garbage_collect_sections", + action_flags = action_mux({ + # Put each function and global var in their own linker section. + ACTION_NAME_GROUPS.all_cc_compile_actions: ["-ffunction-sections", "-fdata-sections"], + # Remove unused functions/symbols from linked binary. + ACTION_NAME_GROUPS.all_cc_link_actions: ["-Wl,--gc-sections"], + }), + doc = "Place each function in it's own section so that the linker can discard unused functions", +) + +cc_feature( + name = "dependency_file", + enabled = True, + action_flags = action_mux({ + (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): [ + "-MD", + "-MF", + "%{dependency_file}", + ] + }) +) + + diff --git a/cc/features.bzl b/cc/features.bzl index 1c7e2a2..6b06bf3 100644 --- a/cc/features.bzl +++ b/cc/features.bzl @@ -2,11 +2,79 @@ load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", - "FeatureInfo", + "FeatureSetInfo", "feature", + "feature_set", "flag_group", "flag_set", ) +load( + "@rules_cc//cc:action_names.bzl", + "ACTION_NAMES", + "ACTION_NAME_GROUPS", +) + +ALL_COMPILE_ACTIONS = ACTION_NAME_GROUPS.all_cc_compile_actions + \ + ACTION_NAME_GROUPS.all_cpp_compile_actions + +ALL_SYSROOT_ACTIONS = ACTION_NAME_GROUPS.all_cc_compile_actions + \ + ACTION_NAME_GROUPS.all_cc_link_actions + \ + ACTION_NAME_GROUPS.all_cpp_compile_actions + \ + ACTION_NAME_GROUPS.cc_link_executable_actions + \ + ACTION_NAME_GROUPS.dynamic_library_link_actions + \ + ACTION_NAME_GROUPS.nodeps_dynamic_library_link_actions + \ + ACTION_NAME_GROUPS.transitive_link_actions + +ALL_LINK_ACTIONS = ACTION_NAME_GROUPS.all_cc_link_actions + \ + ACTION_NAME_GROUPS.cc_link_executable_actions + \ + ACTION_NAME_GROUPS.dynamic_library_link_actions + +# Extracted from: https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables +FLAG_VARS = { + "%{source_file}": ALL_COMPILE_ACTIONS, + "%{input_file}": ACTION_NAMES.strip, + "%{output_file}": ALL_COMPILE_ACTIONS, + "%{output_assembly_file}": ALL_COMPILE_ACTIONS, + "%{output_preprocess_file}": ALL_COMPILE_ACTIONS, + "%{dependency_file}": ALL_COMPILE_ACTIONS, + "%{pic}": ALL_COMPILE_ACTIONS, + "%{gcov_gcno_file}": ALL_COMPILE_ACTIONS, + "%{per_object_debug_info_file}": ALL_COMPILE_ACTIONS, + "%{sysroot}": ALL_SYSROOT_ACTIONS, + "%{def_file_path}": ALL_LINK_ACTIONS, + "%{linker_param_file}": ALL_LINK_ACTIONS, + "%{output_execpath}": ALL_LINK_ACTIONS, + "%{generate_interface_library}": ALL_LINK_ACTIONS, + "%{interface_library_builder_path}": ALL_LINK_ACTIONS, + "%{interface_library_input_path}": ALL_LINK_ACTIONS, + "%{interface_library_output_path}": ALL_LINK_ACTIONS, + "%{force_pic}": ALL_LINK_ACTIONS, + "%{strip_debug_symbols}": ALL_LINK_ACTIONS, + "%{is_cc_test}": ALL_LINK_ACTIONS, + "%{is_using_fission}": ALL_LINK_ACTIONS + ALL_LINK_ACTIONS, + "%{fdo_instrument_path}": ALL_COMPILE_ACTIONS + ALL_LINK_ACTIONS, + "%{fdo_prefetch_hints_path}": ALL_COMPILE_ACTIONS, + "%{csfdo_instrument_path}": ALL_COMPILE_ACTIONS + ALL_LINK_ACTIONS, +} + +# Extracted from: https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables +FLAG_SEQUENCE_VARS = { + "%{includes}": ALL_COMPILE_ACTIONS, + "%{include_paths}": ALL_COMPILE_ACTIONS, + "%{quote_include_paths}": ALL_COMPILE_ACTIONS, + "%{system_include_paths}": ALL_COMPILE_ACTIONS, + "%{preprocessor_defines}": ALL_COMPILE_ACTIONS, + "%{striptopts}": ACTION_NAMES.strip, + "%{legacy_compile_flags}": ALL_COMPILE_ACTIONS, + "%{user_compile_flags}": ALL_COMPILE_ACTIONS, + "%{unfiltered_compile_flags}": ALL_COMPILE_ACTIONS, + "%{runtime_library_search_directories}": ALL_LINK_ACTIONS, + "%{library_search_directories}": ALL_LINK_ACTIONS, + "%{libraries_to_link}": ALL_LINK_ACTIONS, + "%{legacy_link_flags}": ALL_LINK_ACTIONS, + "%{user_link_flags}": ALL_LINK_ACTIONS, + "%{linkstamp_flags}": ALL_LINK_ACTIONS, +} def action_mux(action_map): """ Expands an iterable set of actions that map to a given flag. @@ -19,6 +87,46 @@ def action_mux(action_map): action_flags[action] = action_flags.get(action, []) + flags return action_flags +BUILD_VARS_DOC_URL = "https://bazel.build/docs/cc-toolchain-config-reference" + +def _extract_build_var_from_flag(flag): + var_start_index = flag.find("%{") + if var_start_index == -1: + return None + var_end_index = flag.find("}", start = var_start_index) + if var_end_index == -1: + fail( + "In flag '%s' opening variable '%{' was found but got to \ + end of flag and no closing bracket found '}'" % (flag), + ) + else: + allowed_in_variable = list("abcdefghijklmnopqrstuvwxyz_".elems()) + flag_name = flag[var_start_index + 2:var_end_index] + for c in flag_name.elems(): + if c not in allowed_in_variable: + fail( + "Found invalid character: ", + c, + " in variable.", + " The following characters are supported:\n", + allowed_in_variable, + "See the docs for more information: ", + BUILD_VARS_DOC_URL, + ) + + return flag[var_start_index:var_end_index + 1] + +def _validate_build_var_with_action(build_var, action, is_iterated = False): + if is_iterated: + if build_var not in FLAG_SEQUENCE_VARS.keys(): + fail("Build var '%s' is not found or is not a sequence" % build_var) + else: + valid_actions = FLAG_VARS.get(build_var) + if not valid_actions: + fail("Build var '%s' is not found or is a sequence, and requires iteration" % build_var) + if action not in valid_actions: + fail("Build var is used with unsupported action '%s', the following actions are supported for this build var:\n %s" % (action, valid_actions)) + def _action_flags_as_flag_set(action, flags): return flag_set( actions = [action], @@ -27,18 +135,22 @@ def _action_flags_as_flag_set(action, flags): def _cc_feature_impl(ctx): runfiles = ctx.runfiles(files = []) + feature_deps = [] for dep in ctx.attr.deps: runfiles.merge(dep[DefaultInfo].default_runfiles) + if FeatureSetInfo in dep: + feature_deps.extend(dep[FeatureSetInfo].features) + this_feature = feature( + name = str(ctx.label), + enabled = ctx.attr.enabled, + flag_sets = [ + _action_flags_as_flag_set(action, flags) + for action, flags in ctx.attr.action_flags.items() + ], + ) return [ - feature( - name = str(ctx.label), - enabled = ctx.attr.enabled, - flag_sets = [ - _action_flags_as_flag_set(action, flags) - for action, flags in ctx.attr.action_flags.items() - ], - ), + feature_set(features = feature_deps + [this_feature]), DefaultInfo( files = depset(transitive = [ dep[DefaultInfo].files @@ -64,8 +176,11 @@ cc_feature = rule( "deps": attr.label_list( doc = "The set of features that this feature implicitly enables/implies.", ), + "iterative_over_build_var": attr.string( + doc = "Iterate over a build variable" + ) }, - provides = [FeatureInfo], + provides = [FeatureSetInfo], doc = """ Configure a pipeline through each action in the C++ build. diff --git a/cc/toolchain.bzl b/cc/toolchain.bzl new file mode 100644 index 0000000..cdd5ffa --- /dev/null +++ b/cc/toolchain.bzl @@ -0,0 +1,77 @@ +load("@rules_cc//cc:cc_toolchain_config_lib.bzl", "FeatureSetInfo", "tool_path") + +# We are going to use action_confgs for everything as an alternative to +# using tool_paths which just add unneccesary confusion. +_NULL_TOOL_PATHS = [ + tool_path( + name = "gcc", + path = "/usr/bin/clang", + ), + tool_path( + name = "ld", + path = "/usr/bin/ld", + ), + tool_path( + name = "ar", + path = "/bin/false", + ), + tool_path( + name = "cpp", + path = "/bin/false", + ), + tool_path( + name = "gcov", + path = "/bin/false", + ), + tool_path( + name = "nm", + path = "/bin/false", + ), + tool_path( + name = "objdump", + path = "/bin/false", + ), + tool_path( + name = "strip", + path = "/bin/false", + ), +] + +def _toolchain_config_impl(ctx): + features = [] + for feature in ctx.attr.features: + features.extend(feature[FeatureSetInfo].features) + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, # NEW + # Unused let features handle this. + cxx_builtin_include_directories = [], + tool_paths = _NULL_TOOL_PATHS, + + # TODO: Do we actually need any of these? + toolchain_identifier = "local", + host_system_name = "local", + target_system_name = "local", + target_cpu = "unknown", + target_libc = "unknown", + compiler = "unknown", + abi_version = "unknown", + abi_libc_version = "unknown", + ) + +cc_toolchain_config = rule( + _toolchain_config_impl, + attrs = { + "cc_features": attr.label_list( + doc = "The list of cc_features to include in this toolchain.", + mandatory = True, + providers = [FeatureSetInfo], + ), + "action_configs": attr.label_list( + doc = "The list of cc_action_configs to include in this toolchain.", + mandatory = True, + ), + }, + provides = [CcToolchainConfigInfo], +) diff --git a/third_party/clang_llvm_x86_64_linux_gnu_ubuntu.BUILD b/third_party/clang_llvm_x86_64_linux_gnu_ubuntu.BUILD index e42505d..3032d9d 100644 --- a/third_party/clang_llvm_x86_64_linux_gnu_ubuntu.BUILD +++ b/third_party/clang_llvm_x86_64_linux_gnu_ubuntu.BUILD @@ -1,6 +1,46 @@ +load("@bazel_skylib//rules:native_binary.bzl", "native_binary") +package(default_visibility = ["//visibility:public"]) + exports_files(glob(["**/*"])) filegroup( name = "all_files", srcs = glob(["**/*"]), ) + +TOOLS = { + "cxx": { + "tool": "//:bin/clang++", + "data": ["//:cc"], + }, + "cc": { + "tool": "//:bin/clang", + "data": ["//:ld"], + }, + "ld": { + "tool": "//:bin/ld.lld", + "data": [], + }, + "ar": { + "tool": "//:bin/llvm-ar", + "data": [], + }, + "objdump": { + "tool": "//:bin/llvm-objdump", + "data": [], + }, + "strip": { + "tool": "//:bin/llvm-strip", + "data": [], + }, +} + +[ + native_binary( + name = name, + src = info["tool"], + data = info["data"], + out = name, + ) + for name, info in TOOLS.items() +]