diff --git a/.bazelrc b/.bazelrc index e661e83..fdcebb7 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,3 +1,4 @@ build --incompatible_enable_cc_toolchain_resolution +build --incompatible_strict_action_env build --cxxopt=-std=c++17 build:bzlmod --experimental_enable_bzlmod diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 842584b..1f2eab5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,7 @@ jobs: runs-on: ubuntu-20.04 needs: fetch_test_deps strategy: + fail-fast: false matrix: lib: - "@com_google_googletest//..." diff --git a/WORKSPACE b/WORKSPACE index e2fe5d8..7d44295 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -42,3 +42,5 @@ http_archive( build_file = "@//third_party:clang_llvm_x86_64_linux_gnu_ubuntu.BUILD", strip_prefix = "clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04", ) + +register_toolchains("//...") diff --git a/cc/BUILD.bazel b/cc/BUILD.bazel index 50d1ecc..c0e4708 100644 --- a/cc/BUILD.bazel +++ b/cc/BUILD.bazel @@ -1,25 +1,57 @@ -load("@modular_cc_toolchain//cc:features.bzl", "cc_feature", "action_mux") +load("@modular_cc_toolchain//cc:features.bzl", "action_flag_mux", "cc_feature") load("//cc:action_names.bzl", "ACTION_NAME_GROUPS") -load("//cc:actions.bzl", "cc_action_config") +load("//cc:actions.bzl", "action_tool_mux", "cc_action_config") +load("//cc:cc_toolchain_config.bzl", "cc_toolchain_config") load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") cc_feature( name = "garbage_collect_sections", - action_flags = action_mux({ + enabled = True, + action_flags = action_flag_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", - deps = [":default_clang"], +) + +cc_feature( + name = "no_std_includes", + enabled = True, + action_flags = action_flag_mux({ + ACTION_NAME_GROUPS.all_cc_compile_actions: [ + "-nostdinc", + "-nostdinc++", + "-isystem/usr/include/c++/9", + "-isystem/usr/include/x86_64-linux-gnu/c++/9", + "-isystem/usr/include/c++/9/backward", + "-isystem/usr/lib/gcc/x86_64-linux-gnu/9/include", + "-isystem/usr/local/include", + "-isystem/usr/include/x86_64-linux-gnu", + "-isystem/usr/include", + ], + }), + doc = "Don't use compiler builting include paths.", +) + +cc_feature( + name = "link_cpp_stdlib", + enabled = True, + action_flags = { + ACTION_NAMES.cpp_link_executable: ["-lstdc++"], + }, ) cc_action_config( name = "default_clang", action_tools = { ACTION_NAMES.c_compile: "@clang_llvm_x86_64_linux_gnu_ubuntu//:bin/clang", - } + ACTION_NAMES.cpp_compile: "@clang_llvm_x86_64_linux_gnu_ubuntu//:bin/clang++", + ACTION_NAMES.linkstamp_compile: "@clang_llvm_x86_64_linux_gnu_ubuntu//:bin/clang", + ACTION_NAMES.cpp_link_static_library: "@clang_llvm_x86_64_linux_gnu_ubuntu//:bin/llvm-ar", + ACTION_NAMES.cpp_link_executable: "@clang_llvm_x86_64_linux_gnu_ubuntu//:bin/clang++", + }, ) filegroup( @@ -28,4 +60,60 @@ filegroup( visibility = ["//visibility:public"], ) -exports_files(glob(["*.bzl"])) \ No newline at end of file +exports_files(glob(["*.bzl"])) + +cc_toolchain_config( + name = "clang_config", + action_configs = [ + ":default_clang", + ], + cc_features = [ + ":garbage_collect_sections", + ":link_cpp_stdlib", + ":no_std_includes", + ], + cxx_builtin_include_directories = [ + "/usr/include/c++/9", + "/usr/include/x86_64-linux-gnu/c++/9", + "/usr/include/c++/9/backward", + "/usr/lib/gcc/x86_64-linux-gnu/9/include", + "/usr/local/include", + "/usr/include/x86_64-linux-gnu", + "/usr/include", + ], +) + +cc_toolchain( + name = "clang_toolchain", + toolchain_identifier = "empty", + toolchain_config = ":clang_config", + all_files = ":all_toolchain_files", + ar_files = ":all_toolchain_files", + compiler_files = ":all_toolchain_files", + dwp_files = ":all_toolchain_files", + linker_files = ":all_toolchain_files", + objcopy_files = ":all_toolchain_files", + strip_files = ":all_toolchain_files", + # TODO: This should be enabled for clang though it's easier to debug without it + supports_param_files = 0, + supports_header_parsing = 1, +) + +filegroup( + name = "all_toolchain_files", + srcs = [":clang_config", "@clang_llvm_x86_64_linux_gnu_ubuntu//:all_files"], +) + +toolchain( + name = "linux_clang", + exec_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], + toolchain = ":clang_toolchain", + toolchain_type = "@rules_cc//cc:toolchain_type", +) diff --git a/cc/actions.bzl b/cc/actions.bzl index b836c44..cd7f9d2 100644 --- a/cc/actions.bzl +++ b/cc/actions.bzl @@ -1,40 +1,78 @@ load("@rules_cc//cc:cc_toolchain_config_lib.bzl", "action_config", "tool") -load("//cc:features.bzl", "cc_feature") + +def action_tool_mux(action_tool_mapping): + result = {} + for actions, tool in action_tool_mapping.items(): + if type(actions) == type(""): + result[actions] = tool + elif type(actions) == type(()): + for action in actions: + result[action] = tool + else: + fail("Unsupported key-type, got type:{}, wanted string or tuple of strings".format(type(actions))) + return result + +ActionConfigSetInfo = provider( + doc = "A set of action_configs", + fields = { + "configs": "A set of action configs", + }, +) def _action_config_impl(ctx): - tool_symlink = ctx.actions.declare_file(ctx.label.name) - ctx.actions.symlink(output=tool_symlink, - target_file=ctx.executable.tool, - is_executable=True) - runfiles = ctx.runfiles(files = [tool_symlink]) - runfiles = runfiles.merge_all([ctx.attr.tool[DefaultInfo].default_runfiles] + - [dep[DefaultInfo].default_runfiles for dep in ctx.attr.deps]) - return [ - # TODO(#4): Confirm this still works when bazelbuild/rules_cc#72 is - # merged. - action_config( + dependant_action_configs = [] + for dep in ctx.attr.deps: + dependant_action_configs += dep[ActionConfigSetInfo].configs + + if ctx.attr.tool: + tool_symlink = ctx.actions.declare_file(ctx.label.name) + ctx.actions.symlink( + output = tool_symlink, + target_file = ctx.executable.tool, + is_executable = True, + ) + tool_runfiles = [ctx.attr.tool[DefaultInfo].default_runfiles] + tool_symlink = [tool_symlink] + else: + tool_runfiles = [] + tool_symlink = [] + runfiles = ctx.runfiles(files = tool_symlink) + runfiles = runfiles.merge_all(tool_runfiles + + [dep[DefaultInfo].default_runfiles for dep in ctx.attr.deps]) + configs = [] + if ctx.executable.tool: + configs.append(action_config( action_name = ctx.attr.action_name, enabled = ctx.attr.enabled, tools = [tool(tool = ctx.executable.tool)], + )) + return [ + # TODO(#4): Confirm this still works when bazelbuild/rules_cc#72 is + # merged. + ActionConfigSetInfo( + configs = configs + dependant_action_configs, ), DefaultInfo( - executable = tool_symlink, - runfiles = runfiles, - files = depset([tool_symlink]) - ) + runfiles = runfiles, + files = depset( + tool_symlink, + transitive = [ + dep[DefaultInfo].files + for dep in ctx.attr.deps + ], + ), + ), ] - _cc_action_config = rule( _action_config_impl, attrs = { - "action_name" : attr.string( + "action_name": attr.string( doc = "The action name to configure the tool paths", ), "tool": attr.label( doc = "The tool to use for this action.", executable = True, - mandatory = True, cfg = "exec", allow_single_file = True, ), @@ -43,14 +81,18 @@ _cc_action_config = rule( default = False, ), "deps": attr.label_list( - doc = "The list of features and actions that this action enables." - ) + doc = "The list of features and actions that this action enables.", + ), }, doc = "Maps a tool onto a specific action in a cc toolchain.", - executable = True, + provides = [ActionConfigSetInfo], ) def cc_action_config(**kwargs): + # NOTE: Under the current action_config macros we can only specify one action name per + # action_config. This leads to a lot of uneccesary boiler plate, this rule + macro + # allows us to bundle up a set of action_configs so that we can map multiple + # tools->actions. action_tools = kwargs.pop("action_tools") name = kwargs.pop("name") deps = kwargs.pop("deps", []) @@ -63,12 +105,15 @@ def cc_action_config(**kwargs): # using the top-level deps. enabled = False, deps = deps, - **kwargs, + **kwargs ) + # This is a top level feature that is used to enable the iterated action_configs. - cc_feature( + _cc_action_config( name = name, - deps = ["{}.{}".format(name, action_name) - for action_name in action_tools.keys()] + deps, - **kwargs, + deps = [ + "{}.{}".format(name, action_name) + for action_name in action_tools.keys() + ] + deps, + **kwargs ) diff --git a/cc/cc_toolchain_config.bzl b/cc/cc_toolchain_config.bzl new file mode 100644 index 0000000..5e7c5d9 --- /dev/null +++ b/cc/cc_toolchain_config.bzl @@ -0,0 +1,73 @@ +load("@rules_cc//cc:cc_toolchain_config_lib.bzl", "FeatureSetInfo", "feature") +load("//cc:actions.bzl", "ActionConfigSetInfo") + +# TODO(#12): This probably shouldn't exist. +# There are some quirks in how some features are enabled/disabled based on tool_paths. +# As we don't use tool_paths, we need to reimplement some of these quirks? +_QUIRKY_FEATURES = [ + feature( + name = "quirk_enable_archiver_flags", + enabled = True, + implies = ["archiver_flags"], + ) +] + +def _cc_toolchain_config_impl(ctx): + runfiles = ctx.runfiles() + transitive_files = [] + + # Flatten action_configs + action_configs = [] + for action_config_set in ctx.attr.action_configs: + action_configs += action_config_set[ActionConfigSetInfo].configs + runfiles = runfiles.merge_all([action_config_set[DefaultInfo].default_runfiles]) + transitive_files.append(action_config_set[DefaultInfo].files) + + # Deduplicate features + action_configs = depset(action_configs).to_list() + + # Flatten features + features = [] + for feature_set in ctx.attr.cc_features: + features += feature_set[FeatureSetInfo].features + runfiles = runfiles.merge_all([feature_set[DefaultInfo].default_runfiles]) + transitive_files.append(feature_set[DefaultInfo].files) + + # Deduplicate features + features = depset(features).to_list() + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + toolchain_identifier = "local", + host_system_name = "local", + target_system_name = "local", + target_cpu = "k8", + target_libc = "unknown", + compiler = "clang", + abi_version = "unknown", + abi_libc_version = "unknown", + action_configs = action_configs, + features = features + _QUIRKY_FEATURES, + cxx_builtin_include_directories = ctx.attr.cxx_builtin_include_directories, + ) + +cc_toolchain_config = rule( + _cc_toolchain_config_impl, + attrs = { + "cc_features": attr.label_list( + doc = "A list of features to include in the toolchain", + providers = [FeatureSetInfo], + ), + "action_configs": attr.label_list( + doc = "A list of action_configs to include in the toolchain", + providers = [ActionConfigSetInfo], + mandatory = True, + ), + # TODO(#11): We shouldn't allow this, hard coded system libs make it + # harder to create hermetic toolchains. This should be removed + # before we release the modular toolchains api. + "cxx_builtin_include_directories": attr.string_list( + doc = "A list of system libraries to include", + ), + }, +) diff --git a/cc/features.bzl b/cc/features.bzl index 1c7e2a2..83adfb8 100644 --- a/cc/features.bzl +++ b/cc/features.bzl @@ -2,13 +2,14 @@ load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", - "FeatureInfo", + "FeatureSetInfo", "feature", + "feature_set", "flag_group", "flag_set", ) -def action_mux(action_map): +def action_flag_mux(action_map): """ Expands an iterable set of actions that map to a given flag. See cc_feature documentation for example usage. @@ -27,18 +28,21 @@ def _action_flags_as_flag_set(action, flags): def _cc_feature_impl(ctx): runfiles = ctx.runfiles(files = []) - for dep in ctx.attr.deps: - runfiles.merge(dep[DefaultInfo].default_runfiles) - - return [ - feature( - name = str(ctx.label), + this_feature = feature( + name = str(ctx.label.name), enabled = ctx.attr.enabled, flag_sets = [ _action_flags_as_flag_set(action, flags) for action, flags in ctx.attr.action_flags.items() ], - ), + ) + dep_features = [] + for dep in ctx.attr.deps: + runfiles.merge(dep[DefaultInfo].default_runfiles) + dep_features += dep[FeatureSetInfo].features + + return [ + feature_set([this_feature] + dep_features), DefaultInfo( files = depset(transitive = [ dep[DefaultInfo].files @@ -65,7 +69,7 @@ cc_feature = rule( doc = "The set of features that this feature implicitly enables/implies.", ), }, - provides = [FeatureInfo], + provides = [FeatureSetInfo], doc = """ Configure a pipeline through each action in the C++ build. diff --git a/compilation_tests/BUILD.bazel b/compilation_tests/BUILD.bazel new file mode 100644 index 0000000..3867ec0 --- /dev/null +++ b/compilation_tests/BUILD.bazel @@ -0,0 +1,4 @@ +cc_binary( + name = "simple_binary", + srcs = ["simple_binary.cc"], +) diff --git a/compilation_tests/simple_binary.cc b/compilation_tests/simple_binary.cc new file mode 100644 index 0000000..4cce7f6 --- /dev/null +++ b/compilation_tests/simple_binary.cc @@ -0,0 +1,3 @@ +int main() { + return 0; +} 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..de2c63c 100644 --- a/third_party/clang_llvm_x86_64_linux_gnu_ubuntu.BUILD +++ b/third_party/clang_llvm_x86_64_linux_gnu_ubuntu.BUILD @@ -3,4 +3,5 @@ exports_files(glob(["**/*"])) filegroup( name = "all_files", srcs = glob(["**/*"]), + visibility = ["//visibility:public"], )