diff --git a/.bazelrc b/.bazelrc index 7ac954f112db..dc29e30169e1 100644 --- a/.bazelrc +++ b/.bazelrc @@ -38,11 +38,9 @@ common:non-fatal-checks --per_file_copt=common/check_internal.cpp@-DCARBON_NON_F # enable use of the target config here to make our build and tests more # efficient, see the documentation in //bazel/carbon_rules/BUILD for details. common --flag_alias=use_target_config_carbon_rules=//bazel/carbon_rules:use_target_config_carbon_rules -common --flag_alias=use_target_config_runtimes_builder=//toolchain/driver:use_target_config_runtimes_builder # Bazel doesn't track what commands the flag_alias is valid for, so we can't use # common here. build --use_target_config_carbon_rules -build --use_target_config_runtimes_builder # Default to using a disk cache to minimize re-building LLVM and Clang which we # try to avoid updating too frequently to minimize rebuild cost. The location diff --git a/.github/actions/build-setup-common/action.yml b/.github/actions/build-setup-common/action.yml index fc34d2e15287..4be70370f9c6 100644 --- a/.github/actions/build-setup-common/action.yml +++ b/.github/actions/build-setup-common/action.yml @@ -70,6 +70,13 @@ runs: build --remote_cache=https://storage.googleapis.com/carbon-builds-github-v${CACHE_VERSION} build --remote_download_outputs=minimal + # Allow passing targets that are incompatible so that our explicit + # target lists work more like //... wild card patterns in CI. In CI, + # we're using explicit target lists to prune to a minimal set of + # dependencies, and so skipping incompatible targets is the expected + # behavior. + build --skip_incompatible_explicit_targets + # We import a special key into every action in order to key the Bazel # remote cache in a way that avoids collisions between different # runners. Anything that might change the system external to Bazel but diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index e68d77f037f1..9823893b9916 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -85,7 +85,8 @@ jobs: run: | # Decrease the jobs sharply if we see repeated failures to try to # work around transient network errors even if it makes things - # slower. + # slower. Note that we allow passing targets that are incompatible and + # skip thim as-if we were using `//...` style wild card patterns. ./scripts/run_bazel.py \ --attempts=5 --jobs-on-last-attempt=4 \ test -c ${{ matrix.build_mode }} \ diff --git a/BUILD b/BUILD index 78185075bcd9..eb40ce2548cf 100644 --- a/BUILD +++ b/BUILD @@ -2,6 +2,8 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +load("@bazel_skylib//rules:common_settings.bzl", "string_list_setting") + filegroup( name = "clang_tidy_config", srcs = [".clang-tidy"], @@ -13,3 +15,8 @@ alias( name = "generate_compile_commands", actual = "@wolfd_bazel_compile_commands//:generate_compile_commands", ) + +string_list_setting( + name = "original_platforms", + build_setting_default = [], +) diff --git a/MODULE.bazel b/MODULE.bazel index 38b5a08405b5..d1c94bdf5878 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -56,6 +56,8 @@ use_repo(bazel_cc_toolchain, "bazel_cc_toolchain") register_toolchains("@bazel_cc_toolchain//:all") +register_toolchains("//toolchain/install:all") + # TODO: Trying out `wolfd_bazel_compile_commands`, figure out if there are # issues. Once people have had a chance to try and check for problems, remove # one or the other. diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 5350e007768e..b6158e5624fa 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -305,7 +305,7 @@ "moduleExtensions": { "//bazel/cc_toolchains:clang_configuration.bzl%clang_toolchain_extension": { "general": { - "bzlTransitiveDigest": "44/bjnoCpU6JrWf798MGhACH/QjSv0OkcCi/jF+EGg0=", + "bzlTransitiveDigest": "H3RsK0MbgutDMSlPWTwZq4Vk1U5sjDtgJ5MXQxg7GLU=", "usagesDigest": "lTxkeAFhR0iBEa3dg5hWvtd2HFCr5zCJx/fl27A+IKA=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, diff --git a/bazel/carbon_rules/defs.bzl b/bazel/carbon_rules/defs.bzl index 69a83482613e..3c4826c060a2 100644 --- a/bazel/carbon_rules/defs.bzl +++ b/bazel/carbon_rules/defs.bzl @@ -4,20 +4,10 @@ """Provides rules for building Carbon files using the toolchain.""" +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") +load("@rules_cc//cc/common:cc_common.bzl", "cc_common") load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") -def _runtimes_path(runtimes_target): - path = None - for f in runtimes_target: - if f.short_path.endswith("clang_resource_dir/lib"): - path = f.path - break - - if not path: - fail("Could not find the `clang_resource_dir` in target {}".format(runtimes_target.label)) - - return path[:-len("/clang_resource_dir/lib")] - def _carbon_binary_impl(ctx): toolchain_driver = ctx.executable.internal_exec_toolchain_driver toolchain_data = ctx.files.internal_exec_toolchain_data @@ -30,10 +20,12 @@ def _carbon_binary_impl(ctx): toolchain_data = ctx.files.internal_target_toolchain_data prebuilt_runtimes = ctx.files.internal_target_prebuilt_runtimes + # The extra link flags needed. + link_flags = [] + # Pass any C++ flags from our dependencies onto Carbon. dep_flags = [] dep_hdrs = [] - dep_link_flags = [] dep_link_inputs = [] for dep in ctx.attr.deps: if CcInfo in dep: @@ -47,15 +39,16 @@ def _carbon_binary_impl(ctx): dep_flags += ["--clang-arg=-isystem{0}".format(path) for path in cc_info.compilation_context.system_includes.to_list()] dep_hdrs.append(cc_info.compilation_context.headers) for link_input in cc_info.linking_context.linker_inputs.to_list(): - # TODO: `carbon link` doesn't support linker flags yet. - # dep_link_flags += link_input.user_link_flags + link_flags += link_input.user_link_flags dep_link_inputs += link_input.additional_inputs for lib in link_input.libraries: dep_link_inputs += [dep for dep in [lib.dynamic_library, lib.static_library] if dep] dep_link_inputs += lib.objects if DefaultInfo in dep: dep_link_inputs += dep[DefaultInfo].files.to_list() - dep_link_flags += [dep.path for dep in dep_link_inputs] + + # Add the dependencies' link flags and inputs to the link flags. + link_flags += [dep.path for dep in dep_link_inputs] # Build object files for the prelude and for the binary itself. # TODO: Eventually the prelude should be build as a separate `carbon_library`. @@ -95,13 +88,41 @@ def _carbon_binary_impl(ctx): progress_message = "Compiling " + src.short_path, ) + # Add the Carbon object files to the link flags. + link_flags += [o.path for o in objs] + bin = ctx.actions.declare_file(ctx.label.name) + + # Get all link options from the toolchain and dependencies using standard pattern. + cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo] + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + variables = cc_common.create_link_variables( + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + is_using_linker = True, + user_link_flags = link_flags + [ + # TODO: Remove once the sanitizer runtimes are available. + "-fno-sanitize=all", + ], + output_file = bin.path, + ) + full_link_flags = cc_common.get_memory_inefficient_command_line( + feature_configuration = feature_configuration, + action_name = ACTION_NAMES.cpp_link_executable, + variables = variables, + ) + ctx.actions.run( outputs = [bin], inputs = objs + dep_link_inputs, executable = toolchain_driver, tools = depset(toolchain_data + prebuilt_runtimes), - arguments = ["--prebuilt-runtimes=" + _runtimes_path(prebuilt_runtimes), "link", "--output=" + bin.path] + ["--"] + dep_link_flags + [o.path for o in objs], + arguments = full_link_flags, mnemonic = "CarbonLink", progress_message = "Linking " + bin.short_path, ) @@ -148,9 +169,10 @@ _carbon_binary_internal = rule( ), "prelude_srcs": attr.label_list(allow_files = [".carbon"]), "srcs": attr.label_list(allow_files = [".carbon"]), - "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"), + "_cc_toolchain": attr.label(default = "//toolchain/install:carbon_stage1_cc_toolchain"), }, executable = True, + fragments = ["cpp"], ) def carbon_binary(name, srcs, deps = [], flags = [], tags = []): @@ -177,7 +199,7 @@ def carbon_binary(name, srcs, deps = [], flags = [], tags = []): # `select` which one we use. internal_exec_toolchain_driver = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": None, - "//conditions:default": "//toolchain/install:prefix/bin/carbon", + "//conditions:default": "//toolchain/install:carbon-busybox", }), internal_exec_toolchain_data = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": None, @@ -185,10 +207,10 @@ def carbon_binary(name, srcs, deps = [], flags = [], tags = []): }), internal_exec_prebuilt_runtimes = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": None, - "//conditions:default": "//toolchain/driver:prebuilt_runtimes", + "//conditions:default": "//toolchain/install:built_runtimes", }), internal_target_toolchain_driver = select({ - "//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/install:prefix/bin/carbon", + "//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/install:carbon-busybox", "//conditions:default": None, }), internal_target_toolchain_data = select({ @@ -196,7 +218,7 @@ def carbon_binary(name, srcs, deps = [], flags = [], tags = []): "//conditions:default": None, }), internal_target_prebuilt_runtimes = select({ - "//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/driver:prebuilt_runtimes", + "//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/install:built_runtimes", "//conditions:default": None, }), ) diff --git a/bazel/cc_toolchains/BUILD b/bazel/cc_toolchains/BUILD index e52438a85d1c..f605a721df7e 100644 --- a/bazel/cc_toolchains/BUILD +++ b/bazel/cc_toolchains/BUILD @@ -6,6 +6,26 @@ load("@bazel_skylib//lib:selects.bzl", "selects") package(default_visibility = ["//visibility:public"]) +constraint_setting( + name = "bootstrap_stage", + default_constraint_value = ":stage0", +) + +constraint_value( + name = "stage0", + constraint_setting = ":bootstrap_stage", +) + +constraint_value( + name = "stage1", + constraint_setting = ":bootstrap_stage", +) + +constraint_value( + name = "stage2", + constraint_setting = ":bootstrap_stage", +) + # For use by defs.bzl. # Matches when asan is enabled on a macOS platform. selects.config_setting_group( @@ -50,6 +70,7 @@ config_setting( filegroup( name = "installed_cc_toolchain_starlark", srcs = [ + "carbon_cc_toolchain_config.bzl", "cc_toolchain_actions.bzl", "cc_toolchain_base_features.bzl", "cc_toolchain_config_features.bzl", @@ -61,5 +82,9 @@ filegroup( "cc_toolchain_optimization.bzl", "cc_toolchain_sanitizer_features.bzl", "cc_toolchain_tools.bzl", + + # TODO: Remove this once we can remove the use of it from Carbon + # toolchain rules. + "cc_toolchain_carbon_project_features.bzl", ], ) diff --git a/bazel/cc_toolchains/carbon_cc_toolchain_config.bzl b/bazel/cc_toolchains/carbon_cc_toolchain_config.bzl new file mode 100644 index 000000000000..b32ef88c450e --- /dev/null +++ b/bazel/cc_toolchains/carbon_cc_toolchain_config.bzl @@ -0,0 +1,461 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Starlark cc_toolchain configuration rules for using the Carbon toolchain""" + +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") +load( + "@rules_cc//cc:cc_toolchain_config_lib.bzl", + "action_config", + "flag_group", + "flag_set", + "tool", +) +load( + "@rules_cc//cc:defs.bzl", + "CcToolchainConfigInfo", + "cc_toolchain", +) +load("@rules_cc//cc/common:cc_common.bzl", "cc_common") +load( + "carbon_clang_variables.bzl", + "clang_include_dirs", + "clang_resource_dir", + "clang_sysroot", +) +load( + "cc_toolchain_actions.bzl", + "all_c_compile_actions", + "all_cpp_compile_actions", + "all_link_actions", +) +load("cc_toolchain_carbon_project_features.bzl", "carbon_project_features") +load("cc_toolchain_features.bzl", "clang_cc_toolchain_features") + +def _make_action_configs(tools, runtimes_path = None): + runtimes_flag = "--no-build-runtimes" + if runtimes_path: + runtimes_flag = "--prebuilt-runtimes={0}".format(runtimes_path) + + return [ + action_config( + action_name = name, + enabled = True, + tools = [tools.clang], + ) + for name in all_c_compile_actions + ] + [ + action_config( + action_name = name, + enabled = True, + tools = [tools.clangpp], + ) + for name in all_cpp_compile_actions + ] + [ + action_config( + action_name = name, + enabled = True, + tools = [tools.carbon_busybox], + flag_sets = [flag_set(flag_groups = [flag_group(flags = [ + runtimes_flag, + "link", + # We want to allow Bazel to intermingle linked object files and + # Clang-spelled link flags. The first `--` starts the list of + # initial object files by ending flags to the `link` subcommand, + # and the second `--` switches to Clang-spelled flags. + "--", + "--", + ])])], + ) + for name in all_link_actions + ] + [ + action_config( + action_name = name, + enabled = True, + tools = [tools.llvm_ar], + ) + for name in [ACTION_NAMES.cpp_link_static_library] + ] + [ + action_config( + action_name = name, + enabled = True, + tools = [tools.llvm_strip], + ) + for name in [ACTION_NAMES.strip] + ] + +def _compute_clang_system_include_dirs(): + system_include_dirs_start_index = None + for index, dir in enumerate(clang_include_dirs): + # Skip over the include search directories until we find the resource + # directory. The system include directories are everything after that. + if dir.startswith(clang_resource_dir): + system_include_dirs_start_index = index + 1 + break + if not system_include_dirs_start_index: + fail("Could not find the resource directory in the clang include " + + "directories: {}".format(clang_include_dirs)) + return clang_include_dirs[system_include_dirs_start_index:] + +def _carbon_cc_toolchain_config_impl(ctx): + tools = struct( + carbon_busybox = tool(path = "carbon-busybox"), + clang = tool(path = "llvm/bin/clang"), + clangpp = tool(path = "llvm/bin/clang++"), + llvm_ar = tool(path = "llvm/bin/llvm-ar"), + llvm_strip = tool(path = "llvm/bin/llvm-strip"), + ) + if ctx.attr.bins: + carbon_busybox = None + clang = None + clangpp = None + llvm_ar = None + llvm_strip = None + for f in ctx.files.bins: + if f.basename == "carbon-busybox": + carbon_busybox = f + elif f.basename == "clang": + clang = f + elif f.basename == "clang++": + clangpp = f + elif f.basename == "llvm-ar": + llvm_ar = f + elif f.basename == "llvm-strip": + llvm_strip = f + tools = struct( + carbon_busybox = tool(tool = carbon_busybox), + clang = tool(tool = clang), + clangpp = tool(tool = clangpp), + llvm_ar = tool(tool = llvm_ar), + llvm_strip = tool(tool = llvm_strip), + ) + + # Only use a sysroot if a non-trivial one is set in Carbon's config. + builtin_sysroot = None + sysroot_include_search = [] + if clang_sysroot != "None" and clang_sysroot != "/": + builtin_sysroot = clang_sysroot + sysroot_include_search = ["%sysroot%/usr/include"] + + runtimes_path = None + if ctx.attr.runtimes: + for f in ctx.files.runtimes: + if f.basename == "runtimes_root": + runtimes_path = f.dirname + break + if not runtimes_path: + fail("Unable to compute the runtimes path for: {0}".format( + ctx.attr.runtimes, + )) + + identifier = "{0}_toolchain_{1}_{2}".format( + ctx.attr.identifier_prefix, + ctx.attr.target_cpu, + ctx.attr.target_os, + ) + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = clang_cc_toolchain_features( + target_os = ctx.attr.target_os, + target_cpu = ctx.attr.target_cpu, + + # TODO: This should be configured externally rather than here so + # that the install Carbon toolchain doesn't automatically include + # Carbon-project-specific flags. However, that is especially awkward + # to do until we fully migrate to a rules-based toolchain, and the + # project-specific flags are largely harmless at the moment. We also + # omit a meaningful cache key as when using the Carbon toolchain we + # don't need it as it is a hermetic part of Bazel. + project_features = carbon_project_features(cache_key = ""), + ), + action_configs = _make_action_configs(tools, runtimes_path), + cxx_builtin_include_directories = [ + "runtimes/libunwind/include", + "runtimes/libcxx/include", + "runtimes/libcxxabi/include", + clang_resource_dir + "/include", + "runtimes/clang_resource_dir/include", + ] + _compute_clang_system_include_dirs() + sysroot_include_search, + builtin_sysroot = builtin_sysroot, + + # This configuration only supports local non-cross builds so derive + # everything from the target CPU selected. + toolchain_identifier = identifier, + + # This is used to expose a "flag" that `config_setting` rules can use to + # determine if the compiler is Clang. + compiler = "clang", + ) + +carbon_cc_toolchain_config = rule( + implementation = _carbon_cc_toolchain_config_impl, + attrs = { + "bins": attr.label(mandatory = False), + "identifier_prefix": attr.string(mandatory = True), + "runtimes": attr.label(mandatory = False), + "target_cpu": attr.string(mandatory = True), + "target_os": attr.string(mandatory = True), + }, + provides = [CcToolchainConfigInfo], +) + +def _set_platform_transition_impl(settings, attr): + original_platforms = settings["//:original_platforms"] + + # If the requested platform is the special value of the setting where we + # store the original platforms on an initial transition, set the platform to + # the saved list and clear it. Otherwise, we will set the platform to the + # requested one. + if not attr.platform: + return { + "//:original_platforms": [], + "//command_line_option:platforms": original_platforms, + } + + if original_platforms: + # If there is already a saved original platforms list, preserve it. + original_platforms = [str(label) for label in original_platforms] + else: + # If there is no saved original platforms list, save the current one. + current_platforms = settings["//command_line_option:platforms"] + original_platforms = [str(label) for label in current_platforms] + + return { + "//:original_platforms": original_platforms, + "//command_line_option:platforms": [str(attr.platform)], + } + +set_platform_transition = transition( + inputs = [ + "//command_line_option:platforms", + "//:original_platforms", + ], + outputs = [ + "//command_line_option:platforms", + "//:original_platforms", + ], + implementation = _set_platform_transition_impl, +) + +def _set_platform_filegroup_impl(ctx): + return [DefaultInfo(files = depset(ctx.files.srcs))] + +set_platform_filegroup = rule( + implementation = _set_platform_filegroup_impl, + attrs = { + # The platform to use when building the runtimes. + "platform": attr.label(mandatory = False), + + # Mark that our dependencies are built through a transition. + "srcs": attr.label_list(mandatory = True, cfg = set_platform_transition), + + # Enable transitions in this rule. + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, +) + +def carbon_cc_toolchain_suite( + name, + all_hdrs, + base_files, + clang_hdrs, + platforms, + runtimes, + build_stage = None, + base_stage = None, + tags = []): + """Create a toolchain suite that uses the local Clang/LLVM install. + + Args: + name: + The name of the toolchain suite to produce, used as the base of the + names of each component of the toolchain suite. + all_hdrs: A list of header files to include in the toolchain. + base_files: A list of files to include in the toolchain. + build_stage: The stage to use for the build files. + base_stage: The stage to use for the base files. + clang_hdrs: A list of header files to include in the toolchain. + platforms: An array of (os, cpu) pairs to support in the toolchain. + runtimes: A list of runtimes to include in the toolchain. + tags: Tags to apply to the toolchain. + """ + + def _platform_name(os, cpu, name_suffix = ""): + return "{}{}_{}_{}_platform".format(name, name_suffix, os, cpu) + + # Define platforms for each supported OS/CPU pair. + for os, cpus in platforms.items(): + for cpu in cpus: + constraint_values = [ + "@platforms//os:" + os, + "@platforms//cpu:" + cpu, + ] + if base_stage: + native.platform( + name = _platform_name(os, cpu, "_base"), + constraint_values = constraint_values + [base_stage], + ) + if build_stage: + constraint_values.append(build_stage) + native.platform( + name = _platform_name(os, cpu), + constraint_values = constraint_values, + ) + native.platform( + name = _platform_name(os, cpu, "_runtimes"), + constraint_values = constraint_values + [":is_runtimes_build"], + ) + + base_platform_select = None + if base_stage: + base_platform_select = select({ + ":is_{}_{}".format(os, cpu): ":" + _platform_name(os, cpu, "_base") + for os, cpus in platforms.items() + for cpu in cpus + }) + + runtimes_platform_select = select({ + ":is_{}_{}".format(os, cpu): ":" + _platform_name(os, cpu, "_runtimes") + for os, cpus in platforms.items() + for cpu in cpus + }) + + set_platform_filegroup( + name = name + "_base_files", + srcs = base_files, + platform = base_platform_select, + tags = tags, + ) + + set_platform_filegroup( + name = name + "_runtimes_compile_files", + srcs = [":" + name + "_base_files"] + clang_hdrs, + platform = base_platform_select, + tags = tags, + ) + + set_platform_filegroup( + name = name + "_compile_files", + srcs = [":" + name + "_base_files"] + all_hdrs, + platform = base_platform_select, + tags = tags, + ) + + carbon_cc_toolchain_config( + name = name + "_runtimes_toolchain_config", + identifier_prefix = name + "_runtimes", + target_cpu = select({ + # Note that we need to select on both OS and CPU so that we end up + # spelling the CPU in the correct OS-specific ways. + ":is_{}_{}".format(os, cpu): cpu + for os, cpus in platforms.items() + for cpu in cpus + }), + target_os = select({ + "@platforms//os:" + os: os + for os in platforms.keys() + }), + bins = ":" + name + "_base_files", + tags = tags, + ) + + cc_toolchain( + name = name + "_runtimes_cc_toolchain", + all_files = ":" + name + "_runtimes_compile_files", + ar_files = ":" + name + "_base_files", + as_files = ":" + name + "_runtimes_compile_files", + compiler_files = ":" + name + "_runtimes_compile_files", + dwp_files = ":" + name + "_base_files", + linker_files = ":" + name + "_base_files", + objcopy_files = ":" + name + "_base_files", + strip_files = ":" + name + "_base_files", + toolchain_config = ":" + name + "_runtimes_toolchain_config", + toolchain_identifier = select({ + ":is_{}_{}".format(os, cpu): _platform_name(os, cpu, "_runtimes") + for os, cpus in platforms.items() + for cpu in cpus + }), + tags = tags, + ) + + native.toolchain( + name = name + "_runtimes_toolchain", + target_compatible_with = [":is_runtimes_build"], + toolchain = ":" + name + "_runtimes_cc_toolchain", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", + tags = tags, + ) + + set_platform_filegroup( + name = name + "_runtimes", + srcs = [runtimes], + platform = runtimes_platform_select, + tags = tags, + ) + + carbon_cc_toolchain_config( + name = name + "_toolchain_config", + identifier_prefix = name, + target_cpu = select({ + # Note that we need to select on both OS and CPU so that we end up + # spelling the CPU in the correct OS-specific ways. + ":is_{}_{}".format(os, cpu): cpu + for os, cpus in platforms.items() + for cpu in cpus + }), + target_os = select({ + "@platforms//os:" + os: os + for os in platforms.keys() + }), + runtimes = ":" + name + "_runtimes", + bins = ":" + name + "_base_files", + tags = tags, + ) + + native.filegroup( + name = name + "_linker_files", + srcs = [ + ":" + name + "_base_files", + ":" + name + "_runtimes", + ], + tags = tags, + ) + + native.filegroup( + name = name + "_all_files", + srcs = [ + ":" + name + "_compile_files", + ":" + name + "_linker_files", + ], + tags = tags, + ) + + cc_toolchain( + name = name + "_cc_toolchain", + all_files = ":" + name + "_all_files", + ar_files = ":" + name + "_base_files", + as_files = ":" + name + "_compile_files", + compiler_files = ":" + name + "_compile_files", + dwp_files = ":" + name + "_linker_files", + linker_files = ":" + name + "_linker_files", + objcopy_files = ":" + name + "_base_files", + strip_files = ":" + name + "_base_files", + toolchain_config = ":" + name + "_toolchain_config", + toolchain_identifier = select({ + ":is_{}_{}".format(os, cpu): _platform_name(os, cpu) + for os, cpus in platforms.items() + for cpu in cpus + }), + tags = tags, + ) + + native.toolchain( + name = name + "_toolchain", + target_compatible_with = [build_stage] if build_stage else [], + toolchain = ":" + name + "_cc_toolchain", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", + tags = tags, + ) diff --git a/bazel/cc_toolchains/carbon_clang_variables.bzl b/bazel/cc_toolchains/carbon_clang_variables.bzl new file mode 100644 index 000000000000..f5cd351b5e61 --- /dev/null +++ b/bazel/cc_toolchains/carbon_clang_variables.bzl @@ -0,0 +1,20 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""A Starlark file exporting detected Carbon toolchain configuration variables. + +This file gets processed by a repository rule, substituting the `VARIABLE`s with +values, for example using an invocation of `carbon config`. +""" + +load( + "@bazel_cc_toolchain//:clang_detected_variables.bzl", + _clang_include_dirs = "clang_include_dirs", + _clang_resource_dir = "clang_resource_dir", + _sysroot_dir = "sysroot_dir", +) + +clang_include_dirs = _clang_include_dirs +clang_resource_dir = _clang_resource_dir +clang_sysroot = _sysroot_dir diff --git a/toolchain/install/bazel/carbon_runtimes.bzl b/bazel/cc_toolchains/carbon_runtimes.bzl similarity index 94% rename from toolchain/install/bazel/carbon_runtimes.bzl rename to bazel/cc_toolchains/carbon_runtimes.bzl index 699dd966b3cd..0394ab1e49a3 100644 --- a/toolchain/install/bazel/carbon_runtimes.bzl +++ b/bazel/cc_toolchains/carbon_runtimes.bzl @@ -104,9 +104,11 @@ def _carbon_runtimes_impl(ctx): rel_path = hdr.path if hdr.root.path != "": rel_path = _removeprefix_or_fail(rel_path, hdr.root.path + "/") - rel_path = _removeprefix_or_fail(rel_path, hdr.owner.workspace_root + "/") - rel_path = _removeprefix_or_fail(rel_path, hdr.owner.package + "/") - rel_path = _removeprefix_or_fail(rel_path, "include/") + if hdr.owner.workspace_root != "": + rel_path = _removeprefix_or_fail(rel_path, hdr.owner.workspace_root + "/") + if hdr.owner.package != "": + rel_path = _removeprefix_or_fail(rel_path, hdr.owner.package + "/") + rel_path = _removeprefix_or_fail(rel_path, ctx.attr.clang_hdrs_prefix) out_hdr = ctx.actions.declare_file( "{0}/clang_resource_dir/include/{1}".format(prefix, rel_path), @@ -121,6 +123,7 @@ carbon_runtimes = rule( attrs = { "builtins_archive": attr.label(mandatory = True, allow_files = [".a"]), "clang_hdrs": attr.label_list(mandatory = True, allow_files = True), + "clang_hdrs_prefix": attr.string(default = "include/"), "crt_copts": attr.string_list(default = []), "crtbegin_src": attr.label(allow_files = [".c"]), "crtend_src": attr.label(allow_files = [".c"]), diff --git a/bazel/cc_toolchains/cc_toolchain_base_features.bzl b/bazel/cc_toolchains/cc_toolchain_base_features.bzl index 56e98f7e228e..21d18daaa089 100644 --- a/bazel/cc_toolchains/cc_toolchain_base_features.bzl +++ b/bazel/cc_toolchains/cc_toolchain_base_features.bzl @@ -4,6 +4,7 @@ """Definitions used for the base features of a `cc_toolchain_config`.""" +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", @@ -92,12 +93,41 @@ output_flags_feature = feature( ], ) +strip_feature = feature( + name = "strip_flags", + enabled = True, + flag_sets = [flag_set( + actions = [ACTION_NAMES.strip], + flag_groups = [ + flag_group( + flags = ["-S"], + ), + flag_group( + flags = ["-p"], + ), + flag_group( + expand_if_available = "output_file", + flags = ["-o", "%{output_file}"], + ), + flag_group( + iterate_over = "stripopts", + flags = ["%{stripopts}"], + ), + flag_group( + expand_if_available = "input_file", + flags = ["%{input_file}"], + ), + ], + )], +) + base_features = [ dbg_feature, fastbuild_feature, host_feature, no_legacy_features_feature, opt_feature, + strip_feature, supports_pic_feature, supports_dynamic_linker_feature, supports_start_end_lib_feature, diff --git a/bazel/cc_toolchains/clang_cc_toolchain_config.bzl b/bazel/cc_toolchains/clang_cc_toolchain_config.bzl index 2b7a54dd38f4..59b78f1cfe5a 100644 --- a/bazel/cc_toolchains/clang_cc_toolchain_config.bzl +++ b/bazel/cc_toolchains/clang_cc_toolchain_config.bzl @@ -17,7 +17,7 @@ load( load( ":clang_detected_variables.bzl", "clang_bindir", - "clang_include_dirs_list", + "clang_include_dirs", "clang_resource_dir", "clang_version_for_cache", "llvm_bindir", @@ -40,7 +40,7 @@ def _impl(ctx): extra_cpp_features = [libcxx_feature(llvm_bindir, clang_bindir)], ), action_configs = llvm_action_configs(llvm_bindir, clang_bindir), - cxx_builtin_include_directories = clang_include_dirs_list + [ + cxx_builtin_include_directories = clang_include_dirs + [ # Add Clang's resource directory to the end of the builtin include # directories to cover the use of sanitizer resource files by the # driver. @@ -109,7 +109,7 @@ def cc_local_toolchain_suite(name, configs): native.toolchain( name = config_name, exec_compatible_with = compatible_with, - target_compatible_with = compatible_with, + target_compatible_with = compatible_with + ["@carbon//bazel/cc_toolchains:stage0"], toolchain = config_name + "_tools", toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", ) diff --git a/bazel/cc_toolchains/clang_configuration.bzl b/bazel/cc_toolchains/clang_configuration.bzl index d8619850ea0c..1c3d685d28b1 100644 --- a/bazel/cc_toolchains/clang_configuration.bzl +++ b/bazel/cc_toolchains/clang_configuration.bzl @@ -226,7 +226,7 @@ def _configure_clang_toolchain_impl(repository_ctx): repository_ctx.attr._clang_detected_variables_template, substitutions = { "{CLANG_BINDIR}": str(clang.dirname), - "{CLANG_INCLUDE_DIRS_LIST}": str( + "{CLANG_INCLUDE_DIRS}": str( [str(path) for path in include_dirs], ), "{CLANG_RESOURCE_DIR}": resource_dir, diff --git a/bazel/cc_toolchains/clang_detected_variables.tpl.bzl b/bazel/cc_toolchains/clang_detected_variables.tpl.bzl index 3cb83b275950..2024500d0088 100644 --- a/bazel/cc_toolchains/clang_detected_variables.tpl.bzl +++ b/bazel/cc_toolchains/clang_detected_variables.tpl.bzl @@ -14,5 +14,5 @@ clang_bindir = "{CLANG_BINDIR}" clang_version = {CLANG_VERSION} clang_version_for_cache = "{CLANG_VERSION_FOR_CACHE}" clang_resource_dir = "{CLANG_RESOURCE_DIR}" -clang_include_dirs_list = {CLANG_INCLUDE_DIRS_LIST} +clang_include_dirs = {CLANG_INCLUDE_DIRS} sysroot_dir = "{SYSROOT}" diff --git a/bazel/cc_toolchains/make_include_copts.bzl b/bazel/cc_toolchains/make_include_copts.bzl new file mode 100644 index 000000000000..30bc4721d385 --- /dev/null +++ b/bazel/cc_toolchains/make_include_copts.bzl @@ -0,0 +1,25 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Workaround for lack of easy way to add private includes. + +This is a workaround for the following Bazel issue: +https://github.com/bazelbuild/bazel/issues/2670 +""" + +def make_include_copts(include_dirs): + """Create `copts` to search the provided directories for includes.""" + copts = [] + prefix = "" + + repo_name = native.repository_name() + if repo_name != "@": + prefix = "external/{}/".format(repo_name[1:]) + + package_name = native.package_name() + for include_dir in include_dirs: + copts.append("-I{}{}/{}".format(prefix, package_name, include_dir)) + copts.append("-I$(GENDIR)/{}{}/{}".format(prefix, package_name, include_dir)) + + return copts diff --git a/bazel/manifest/defs.bzl b/bazel/manifest/defs.bzl index d3981a76cc2f..d09bfdf2ea61 100644 --- a/bazel/manifest/defs.bzl +++ b/bazel/manifest/defs.bzl @@ -8,10 +8,6 @@ def _get_files(ctx): files = [] for src in ctx.attr.srcs: files.extend([f.path for f in src[DefaultInfo].files.to_list()]) - files.extend([ - f.path - for f in src[DefaultInfo].default_runfiles.files.to_list() - ]) if ctx.attr.strip_package_dir: # Files may or may not be prefixed with the bin directory, and then diff --git a/examples/bazel_test_runner.py b/examples/bazel_test_runner.py index ee307c73396a..8dcf60310e78 100755 --- a/examples/bazel_test_runner.py +++ b/examples/bazel_test_runner.py @@ -21,7 +21,7 @@ class BazelExampleTest(test_base.TestBase): test_base.TestBase.setUp(self) self.runfiles = runfiles.Create() self.install_module = self.runfiles.Rlocation( - "carbon/toolchain/install/prefix/lib/carbon" + "carbon/toolchain/install" ) self.startup_flags = [ "--ignore_all_rc_files", @@ -62,7 +62,9 @@ class BazelExampleTest(test_base.TestBase): # Retry transient errors with a brief delay. print(f"Attempt {attempt + 1} failed with exit code {exit_code}") time.sleep(attempt) + self.AssertExitCode(exit_code, 0, stderr) + return stdout def test_compile_lib(self) -> None: # TODO: Can remove this in favor of always running `test_run` if we can diff --git a/scripts/fix_cc_deps.py b/scripts/fix_cc_deps.py index bb09b138158a..9f80f402b134 100755 --- a/scripts/fix_cc_deps.py +++ b/scripts/fix_cc_deps.py @@ -295,6 +295,18 @@ def main() -> None: all_missing_deps: list[tuple[str, set[str]]] = [] any_ambiguous = False for rule_name, rule in carbon_rules.items(): + # Skip rules building runtimes as the rules that provide their sources + # are not analyzed by this script. + if rule_name in [ + "//toolchain/install:builtins", + "//toolchain/install:builtins_internal", + "//toolchain/install:libc_internal_libcxx", + "//toolchain/install:libcxx", + "//toolchain/install:libcxxabi_internal", + "//toolchain/install:libcxxabi", + "//toolchain/install:libunwind", + ]: + continue missing_deps, ambiguous = get_missing_deps( header_to_rule_map, generated_files, rule ) diff --git a/toolchain/BUILD b/toolchain/BUILD index 29ae5be1e702..9458a456d0bb 100644 --- a/toolchain/BUILD +++ b/toolchain/BUILD @@ -11,7 +11,7 @@ run_tool( name = "carbon", data = ["//toolchain/install:install_data"], env = cc_env(), - tool = "//toolchain/install:prefix/bin/carbon", + tool = "//toolchain/install:carbon-busybox", ) # A convenience target for running the toolchain with the full prelude diff --git a/toolchain/base/install_paths.cpp b/toolchain/base/install_paths.cpp index be0c9193102b..130eee603032 100644 --- a/toolchain/base/install_paths.cpp +++ b/toolchain/base/install_paths.cpp @@ -21,8 +21,7 @@ namespace Carbon { // The location within our Bazel output tree of the install root. -static constexpr llvm::StringLiteral BazelRoot = - "carbon/toolchain/install/prefix/lib/carbon/"; +static constexpr llvm::StringLiteral BazelRoot = "carbon/toolchain/install/"; // Path within an install root for our marker of a valid install. static constexpr llvm::StringLiteral MarkerPath = "carbon_install.txt"; @@ -84,7 +83,7 @@ auto InstallPaths::ReadClangHeadersManifest() const // root. Consider whether this manifest should be within the install or // consider moving the code to access it to be separate and specific to the // infrastructure needing it. - return ReadManifest(root_ / "../../..", "clang_headers_manifest.txt"); + return ReadManifest(root_, "clang_headers_manifest.txt"); } auto InstallPaths::ReadManifest(std::filesystem::path manifest_path, diff --git a/toolchain/base/install_paths.h b/toolchain/base/install_paths.h index 50f671b736f8..efb73830dfd4 100644 --- a/toolchain/base/install_paths.h +++ b/toolchain/base/install_paths.h @@ -148,7 +148,7 @@ class InstallPaths { auto SetError(llvm::Twine message) -> void; // Check that the install paths have a marker file at - // `root()/lib/carbon/carbon_install.txt". If not, calls `SetError` with the + // `root()/carbon_install.txt". If not, calls `SetError` with the // relevant error message. auto CheckMarkerFile() -> void; @@ -162,7 +162,7 @@ class InstallPaths { // // When run from Bazel (for example, in unit tests or development binaries) // this will look like: - // `bazel-bin/some/bazel/target.runfiles/_main/toolchain/install/prefix/lib/carbon` + // `bazel-bin/some/bazel/target.runfiles/_main/toolchain/install` // // When installed, it's expected to be similar to the CMake install prefix, // followed by `lib/carbon`: @@ -177,7 +177,7 @@ class InstallPaths { // our installation to behave in a similar and compatible way. // // The hierarchy of files beneath the install root can be found in the - // BUILD's `install_dirs` entry for `lib/carbon`. + // BUILD rules in `//toolchain/install`. std::filesystem::path root_; // The opened root directory. diff --git a/toolchain/base/install_paths_test.cpp b/toolchain/base/install_paths_test.cpp index 53c9f1a66b96..1f066eaa985d 100644 --- a/toolchain/base/install_paths_test.cpp +++ b/toolchain/base/install_paths_test.cpp @@ -32,7 +32,6 @@ namespace { using ::bazel::tools::cpp::runfiles::Runfiles; using ::testing::_; -using ::testing::EndsWith; using ::testing::Eq; using ::testing::HasSubstr; using Testing::IsSuccess; @@ -61,14 +60,6 @@ class InstallPathsTest : public ::testing::Test { ASSERT_THAT(root_result, IsSuccess(_)); Filesystem::Dir root = *std::move(root_result); - // Check that the root is located in the expected part of the FHS layout. - // TODO: Adjust this to work equally well on Windows. - EXPECT_THAT(root_path.native(), EndsWith("lib/carbon/")); - EXPECT_THAT( - root.Access("../../bin/carbon", Filesystem::AccessCheckFlags::Execute), - IsSuccess(Eq(true))) - << "path: " << (root_path / "../../bin/carbon"); - std::filesystem::path core_package_path = paths.core_package(); ASSERT_THAT(core_package_path, StartsWith(root_path)); EXPECT_THAT(Filesystem::Cwd().Access(core_package_path / "prelude.carbon"), @@ -93,8 +84,8 @@ class InstallPathsTest : public ::testing::Test { }; TEST_F(InstallPathsTest, RootBusybox) { - std::string installed_busybox_path = test_runfiles_->Rlocation( - "carbon/toolchain/install/prefix/lib/carbon/carbon-busybox"); + std::string installed_busybox_path = + test_runfiles_->Rlocation("carbon/toolchain/install/carbon-busybox"); auto paths = InstallPaths::MakeExeRelative(installed_busybox_path); ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error(); @@ -102,8 +93,8 @@ TEST_F(InstallPathsTest, RootBusybox) { } TEST_F(InstallPathsTest, RootExplicit) { - std::string marker_path = test_runfiles_->Rlocation( - "carbon/toolchain/install/prefix/lib/carbon/carbon_install.txt"); + std::string marker_path = + test_runfiles_->Rlocation("carbon/toolchain/install/carbon_install.txt"); llvm::StringRef root_path = marker_path; CARBON_CHECK(root_path.consume_back("carbon_install.txt"), diff --git a/toolchain/base/runtimes_build_info.bzl b/toolchain/base/runtimes_build_info.bzl index 2fefefb5942f..06501ed84e5a 100644 --- a/toolchain/base/runtimes_build_info.bzl +++ b/toolchain/base/runtimes_build_info.bzl @@ -62,16 +62,16 @@ RUNTIMES_TEXTUAL_SRCS_FILEGROUPS = [ ] RUNTIMES_PREFIXES = { - "libcxx_hdrs": "libcxx/", - "libcxx_linux_srcs": "libcxx/", - "libcxx_macos_srcs": "libcxx/", - "libcxx_shared_headers_hdrs": "libc/internal/", - "libcxx_win32_srcs": "libcxx/", - "libcxxabi_hdrs": "libcxxabi/", - "libcxxabi_srcs": "libcxxabi/", - "libcxxabi_textual_srcs": "libcxxabi/", - "libunwind_hdrs": "libunwind/", - "libunwind_srcs": "libunwind/", + "libcxx_hdrs": "runtimes/libcxx/", + "libcxx_linux_srcs": "runtimes/libcxx/", + "libcxx_macos_srcs": "runtimes/libcxx/", + "libcxx_shared_headers_hdrs": "runtimes/libc/internal/", + "libcxx_win32_srcs": "runtimes/libcxx/", + "libcxxabi_hdrs": "runtimes/libcxxabi/", + "libcxxabi_srcs": "runtimes/libcxxabi/", + "libcxxabi_textual_srcs": "runtimes/libcxxabi/", + "libunwind_hdrs": "runtimes/libunwind/", + "libunwind_srcs": "runtimes/libunwind/", } def _get_name(target): @@ -94,7 +94,7 @@ def _builtins_path(file): path = path.removeprefix("compiler-rt/lib/") if not path.startswith("builtins/"): fail("Not a builtins-relative path for: {0}".format(file.path)) - return path + return "runtimes/" + path def _runtimes_path(file): """Returns the install path for a file in a normal runtimes library.""" diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 73751919abd2..f191ca885a33 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -2,12 +2,10 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") load("@rules_shell//shell:sh_test.bzl", "sh_test") load("//bazel/cc_rules:defs.bzl", "cc_binary", "cc_library", "cc_test") load("//bazel/cc_toolchains:defs.bzl", "cc_env") load("//testing/fuzzing:rules.bzl", "cc_fuzz_test") -load(":prebuilt_runtimes.bzl", "prebuilt_runtimes") package(default_visibility = ["//visibility:public"]) @@ -90,7 +88,7 @@ cc_test( size = "medium", srcs = ["clang_runtimes_test.cpp"], data = [ - ":prebuilt_runtimes", + "//toolchain/install:built_runtimes", "//toolchain/install:install_data", ], deps = [ @@ -189,7 +187,7 @@ cc_library( "driver_subcommand.h", ], data = [ - "//toolchain/install:install_data.no_driver", + "//toolchain/install:all_data_files", ], textual_hdrs = ["flags.def"], deps = [ @@ -395,7 +393,7 @@ cc_library( srcs = ["tool_runner_base.cpp"], hdrs = ["tool_runner_base.h"], data = [ - "//toolchain/install:install_data.no_driver", + "//toolchain/install:all_data_files", ], deps = [ "//common:ostream", @@ -404,56 +402,3 @@ cc_library( "@llvm-project//llvm:Support", ], ) - -cc_binary( - name = "bazel_build_clang_runtimes", - srcs = ["bazel_build_clang_runtimes.cpp"], - data = [ - "//toolchain/install:install_data.no_driver", - "@llvm-project//clang:clang", - ], - deps = [ - ":clang_runner", - ":codegen_options", - ":runtimes_cache", - "//common:all_llvm_targets", - "//common:bazel_working_dir", - "//common:check", - "//common:command_line", - "//common:error", - "//common:exe_path", - "//common:filesystem", - "//common:init_llvm", - "//common:raw_string_ostream", - "//common:version", - "//toolchain/base:install_paths", - "@bazel_tools//tools/cpp/runfiles", - "@llvm-project//llvm:Support", - "@llvm-project//llvm:TargetParser", - ], -) - -# Flag controlling whether the target config is used for the -# tools used by the `prebuilt_runtimes` rules. -# -# Using the exec config is more correct and will also optimize the tools used to -# build the runtimes, potentially making them run faster. However, it will -# likely double the number of compiles needed to build everything necessary in -# that configuration. As a consequence, it is useful in development and CI when -# the target config is compatible with the exec config to set this flag. -bool_flag( - name = "use_target_config_runtimes_builder", - build_setting_default = False, -) - -config_setting( - name = "use_target_config_runtimes_builder_config", - flag_values = {":use_target_config_runtimes_builder": "True"}, -) - -# TODO: Correctly set the `target` argument here based on the Bazel target -# platform. Without this, we will generate invalid prebuilt runtimes when cross -# compiling. -prebuilt_runtimes( - name = "prebuilt_runtimes", -) diff --git a/toolchain/driver/bazel_build_clang_runtimes.cpp b/toolchain/driver/bazel_build_clang_runtimes.cpp deleted file mode 100644 index 332ae56e41ca..000000000000 --- a/toolchain/driver/bazel_build_clang_runtimes.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/bazel_working_dir.h" -#include "common/check.h" -#include "common/command_line.h" -#include "common/error.h" -#include "common/exe_path.h" -#include "common/filesystem.h" -#include "common/init_llvm.h" -#include "common/raw_string_ostream.h" -#include "common/version.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/ThreadPool.h" -#include "llvm/Support/Threading.h" -#include "llvm/Support/VirtualFileSystem.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/TargetParser/Triple.h" -#include "toolchain/base/install_paths.h" -#include "toolchain/driver/clang_runner.h" -#include "toolchain/driver/clang_runtimes.h" -#include "toolchain/driver/codegen_options.h" -#include "toolchain/driver/runtimes_cache.h" -#include "tools/cpp/runfiles/runfiles.h" - -namespace Carbon { - -namespace { -struct Options { - static const CommandLine::CommandInfo Info; - - auto Build(CommandLine::CommandBuilder& b) -> void; - - bool verbose = false; - bool force = false; - bool threads = true; - - llvm::StringRef directory; - CodegenOptions codegen_options; -}; -} // namespace - -// Note that this is not constexpr so that it can include information generated -// in separate translation units and potentially overridden at link time in the -// version string. -const CommandLine::CommandInfo Options::Info = { - .name = "bazel_build_clang_runtimes", - .version = Version::ToolchainInfo, - .help = R"""( -A dedicated tool for use with Bazel to build Carbon's _Clang_ runtimes. - -This works similarly to the Carbon `build-runtimes` subcommand with some key -differences: - -1) It only builds the Clang runtimes, not any Carbon-specific runtimes. This is - important due to the next point... - -2) It is a stand-alone command with minimal dependencies on Carbon to allow - Bazel to cache the Clang-based runtimes across most changes to the Carbon - toolchain when the LLVM version stays the same. - -3) It removes any symlinks in the built runtimes tree into other parts of the - installation that are not generated by this command. This allows a Bazel rule - to re-create those in using Bazel-specific logic that connects those parts of - the runtimes tree to their respective inputs. -)""", -}; - -auto Options::Build(CommandLine::CommandBuilder& b) -> void { - b.AddFlag( - { - .name = "verbose", - .short_name = "v", - .help = "Enable verbose logging to the stderr stream.", - }, - [&](CommandLine::FlagBuilder& arg_b) { arg_b.Set(&verbose); }); - - b.AddFlag( - { - .name = "force", - .help = R"""( -Force re-creating the provided output path from scratch - -This will **remove** the provided output path and re-create it from scratch. -)""", - }, - [&](CommandLine::FlagBuilder& arg_b) { arg_b.Set(&force); }); - - b.AddFlag( - { - .name = "threads", - .help = R"""( -Controls whether threads are used to build runtimes. - -When enabled (the default), Carbon will try to build runtime libraries using -threads to parallelize the operation. How many threads is controlled -automatically by the system. - -Disabling threads ensures a single threaded build of the runtimes which can help -when there are errors or other output. -)""", - }, - [&](auto& arg_b) { - arg_b.Default(true); - arg_b.Set(&threads); - }); - - codegen_options.Build(b); - - b.AddStringPositionalArg( - { - .name = "output-directory", - .help = R"""( -The directory to populate with runtime libraries suitable for the selected code -generation options. -)""", - }, - [&](auto& arg_b) { - arg_b.Required(true); - arg_b.Set(&directory); - }); - - b.Do([] {}); -} - -static auto MakeInstallPaths(const std::filesystem::path& exe_path) - -> InstallPaths { - CARBON_CHECK(*Filesystem::Cwd().Access(exe_path, - Filesystem::AccessCheckFlags::Execute), - "Invoked with a non-executable `argv[0]`: {0}", exe_path); - return InstallPaths::MakeForBazelRunfiles(exe_path.native()); -} - -static auto GetClangPath(const std::filesystem::path& exe_path) - -> std::filesystem::path { - std::string runtimes_error; - using bazel::tools::cpp::runfiles::Runfiles; - std::unique_ptr runfiles( - Runfiles::Create(exe_path.native(), &runtimes_error)); - CARBON_CHECK(runfiles != nullptr, "Failed to find runtimes tree: {0}", - runtimes_error); - std::filesystem::path clang_path = - runfiles->Rlocation("llvm-project/clang/clang"); - CARBON_CHECK(!clang_path.empty()); - CARBON_CHECK(*Filesystem::Cwd().Access(clang_path)); - return clang_path; -} - -static auto ParseOptions(int argc, char** argv) -> ErrorOr { - Options options; - llvm::SmallVector args( - llvm::ArrayRef(argv, argc).drop_front()); - CARBON_ASSIGN_OR_RETURN( - auto result, CommandLine::Parse(args, llvm::outs(), Options::Info, - [&](CommandLine::CommandBuilder& b) { - options.Build(b); - })); - if (result == CommandLine::ParseResult::MetaSuccess) { - // Exit immediately with success if this was just a meta invocation. -#if !defined(__APPLE__) - std::quick_exit(0); -#else - // No `std::quick_exit` on macOS, despite the standard including it. - _Exit(0); -#endif - } - return options; -} - -// The actual `main` implementation. Can return an exit code or an `Error` -// (which causes EXIT_FAILURE). -// -// Note that this is primarily an internal utility for use with a specific set -// of Bazel rules, and so many errors are directly `CARBON_CHECK`-ed instead of -// propagated. Only basic command line errors are propagated using the `Error` -// side of the return. Other errors in the execution environment are `CHECK`-ed -// to provide useful backtraces when debugging. -static auto Main(int argc, char** argv) -> ErrorOr { - InitLLVM init_llvm(argc, argv); - if (argc < 1) { - return Error("Invoked without command line arguments"); - } - - std::filesystem::path exe_path = argv[0]; - exe_path = SetWorkingDirForBazelRun(exe_path); - - const auto install_paths = MakeInstallPaths(exe_path); - - CARBON_ASSIGN_OR_RETURN(Options options, ParseOptions(argc, argv)); - - std::filesystem::path clang_path = GetClangPath(exe_path); - auto fs = llvm::vfs::getRealFileSystem(); - llvm::raw_ostream* vlog_stream = options.verbose ? &llvm::errs() : nullptr; - ClangRunner runner(&install_paths, fs, vlog_stream, std::move(clang_path)); - - Runtimes::Cache::Features features = { - .target = options.codegen_options.target.str()}; - - llvm::SingleThreadExecutor single_thread({.ThreadsRequested = 1}); - std::optional threads; - llvm::ThreadPoolInterface* thread_pool = &single_thread; - if (options.threads) { - threads.emplace(llvm::optimal_concurrency()); - thread_pool = &*threads; - } - - auto runtimes = *Runtimes::Make(options.directory.str(), vlog_stream); - - if (options.force) { - // Remove existing runtimes to force a rebuild. - runtimes.Remove(Runtimes::ClangResourceDir).Check(); - runtimes.Remove(Runtimes::LibUnwind).Check(); - runtimes.Remove(Runtimes::Libcxx).Check(); - } - - ClangResourceDirBuilder resource_dir_builder( - &runner, thread_pool, llvm::Triple(features.target), &runtimes); - ClangArchiveRuntimesBuilder lib_unwind_builder( - &runner, thread_pool, llvm::Triple(features.target), &runtimes); - ClangArchiveRuntimesBuilder libcxx_builder( - &runner, thread_pool, llvm::Triple(features.target), &runtimes); - - std::filesystem::path resource_dir_path = - *std::move(resource_dir_builder).Wait(); - std::move(lib_unwind_builder).Wait().Check(); - std::move(libcxx_builder).Wait().Check(); - - // Now remove the `include` symlink from the resource_dir. We'll re-create - // this tree in the Bazel rule, as the symlink currently is an absolute - // (non-hermetic) path. We want Bazel to manage this directory with links to - // the actual input files. - Filesystem::Dir resource_dir = *Filesystem::Cwd().OpenDir(resource_dir_path); - resource_dir.Unlink("include").Check(); - - return EXIT_SUCCESS; -} - -} // namespace Carbon - -auto main(int argc, char** argv) -> int { - auto result = Carbon::Main(argc, argv); - if (result.ok()) { - return *result; - } else { - llvm::errs() << "error: " << result.error() << "\n"; - return EXIT_FAILURE; - } -} diff --git a/toolchain/driver/clang_runtimes.cpp b/toolchain/driver/clang_runtimes.cpp index 83e674dd6617..37255aa02baf 100644 --- a/toolchain/driver/clang_runtimes.cpp +++ b/toolchain/driver/clang_runtimes.cpp @@ -282,7 +282,7 @@ ClangArchiveRuntimesBuilder::ClangArchiveRuntimesBuilder( "Invalid runtimes component for an archive runtime builder."); } - archive_.emplace(this, archive_path_, installation().runtimes_root(), + archive_.emplace(this, archive_path_, installation().root(), CollectSrcFiles(), CollectCflags()); tasks_.async([this]() mutable { Setup(); }); } @@ -374,6 +374,22 @@ auto ClangArchiveRuntimesBuilder::Finish() -> void { template class ClangArchiveRuntimesBuilder; template class ClangArchiveRuntimesBuilder; +auto ClangResourceDirBuilder::GetDarwinOsSuffix(llvm::Triple target_triple) + -> llvm::StringRef { + switch (target_triple.getOS()) { + case llvm::Triple::IOS: + return target_triple.isSimulatorEnvironment() ? "iossim" : "ios"; + case llvm::Triple::WatchOS: + return target_triple.isSimulatorEnvironment() ? "watchossim" : "watchos"; + case llvm::Triple::TvOS: + return target_triple.isSimulatorEnvironment() ? "tvossim" : "tvos"; + case llvm::Triple::XROS: + return target_triple.isSimulatorEnvironment() ? "xrossim" : "xros"; + default: + return "osx"; + } +} + ClangResourceDirBuilder::ClangResourceDirBuilder( ClangRunner* clang, llvm::ThreadPoolInterface* threads, llvm::Triple target_triple, Runtimes* runtimes) @@ -400,7 +416,25 @@ ClangResourceDirBuilder::ClangResourceDirBuilder( } runtimes_builder_ = std::get(std::move(build_dir)); - lib_path_ = std::filesystem::path("lib") / target_triple_.str(); + lib_path_ = std::filesystem::path("lib"); + std::filesystem::path builtins_name = "libclang_rt.builtins.a"; + if (target_triple.isOSDarwin()) { + // Darwin targets don't use the full triple, and don't include the + // architecture in the resource directory naming structure. + // + // TODO: We should add support for embedded Darwin as well which uses a + // different layout. + lib_path_ /= "darwin"; + + // Darwin targets also use a custom naming convention for the builtins + // archive. + builtins_name = + llvm::formatv("libclang_rt.{0}.a", GetDarwinOsSuffix(target_triple_)) + .str(); + } else { + lib_path_ /= target_triple_.str(); + } + // TODO: Currently, we only need a single include path to see headers inside // the `builtins` directory. However, we're anticipating needing more, for // example to support SipHash. If that need doesn't materialize, we should @@ -415,9 +449,8 @@ ClangResourceDirBuilder::ClangResourceDirBuilder( for (const auto& include_path : include_paths_) { copts.append({"-I", include_path.native()}); } - archive_.emplace(this, lib_path_ / "libclang_rt.builtins.a", - installation().runtimes_root(), CollectBuiltinsSrcFiles(), - copts); + archive_.emplace(this, lib_path_ / builtins_name, installation().root(), + CollectBuiltinsSrcFiles(), copts); tasks_.async([this]() { Setup(); }); } @@ -513,7 +546,7 @@ auto ClangResourceDirBuilder::BuildCrtFile(llvm::StringRef src_file) (src_file == RuntimesBuildInfo::CrtBegin ? "clang_rt.crtbegin.o" : "clang_rt.crtend.o"); std::filesystem::path src_path = - installation().runtimes_root() / std::string_view(src_file); + installation().root() / std::string_view(src_file); CARBON_VLOG("Building `{0}' from `{1}`...\n", out_path, src_path); llvm::SmallVector copts = { diff --git a/toolchain/driver/clang_runtimes.h b/toolchain/driver/clang_runtimes.h index 57edfb8f3399..37ca51db2001 100644 --- a/toolchain/driver/clang_runtimes.h +++ b/toolchain/driver/clang_runtimes.h @@ -284,6 +284,10 @@ class ClangResourceDirBuilder : public ClangRuntimesBuilderBase { llvm::Triple target_triple, Runtimes* runtimes); private: + friend class ClangResourceDirBuilderTestPeer; + + static auto GetDarwinOsSuffix(llvm::Triple target_triple) -> llvm::StringRef; + // Helper method to encapsulate the logic of configuring the list of source // files to use in the `builtins` archive within these runtimes on a // particular target. diff --git a/toolchain/driver/clang_runtimes_test.cpp b/toolchain/driver/clang_runtimes_test.cpp index 0eca6ebc6b13..30f33909fd4d 100644 --- a/toolchain/driver/clang_runtimes_test.cpp +++ b/toolchain/driver/clang_runtimes_test.cpp @@ -33,6 +33,14 @@ #include "tools/cpp/runfiles/runfiles.h" namespace Carbon { + +class ClangResourceDirBuilderTestPeer { + public: + static auto GetDarwinOsSuffix(llvm::Triple target_triple) -> llvm::StringRef { + return ClangResourceDirBuilder::GetDarwinOsSuffix(target_triple); + } +}; + namespace { using ::bazel::tools::cpp::runfiles::Runfiles; @@ -172,8 +180,20 @@ class ClangRuntimesTest : public ::testing::Test { // a relevant symbol by running the `llvm-nm` tool over it. Using `nm` // rather than directly inspecting the objects is a bit awkward, but lets us // easily ignore the wrapping in an archive file. + std::filesystem::path lib_path = "lib"; + std::string builtins_name = "libclang_rt.builtins.a"; + if (target_triple_.isOSDarwin()) { + lib_path /= "darwin"; + builtins_name = + llvm::formatv("libclang_rt.{0}.a", + ClangResourceDirBuilderTestPeer::GetDarwinOsSuffix( + target_triple_)) + .str(); + } else { + lib_path /= target_; + } std::filesystem::path builtins_path = - resource_dir_path / "lib" / target_ / "libclang_rt.builtins.a"; + resource_dir_path / lib_path / builtins_name; std::string builtins_symbols = NmListDefinedSymbols(builtins_path); // Check that we found a definition of `__mulodi4`, a builtin function @@ -291,20 +311,20 @@ TEST_F(ClangRuntimesTest, DISABLED_Libcxx) { } TEST_F(ClangRuntimesTest, PrebuiltResourceDir) { - std::filesystem::path prebuilt_runtimes_path = test_runfiles_->Rlocation( - "carbon/toolchain/driver/prebuilt_runtimes_tree"); + std::filesystem::path prebuilt_runtimes_path = + test_runfiles_->Rlocation("carbon/toolchain/install/runtimes"); TestResourceDir(prebuilt_runtimes_path / "clang_resource_dir"); } TEST_F(ClangRuntimesTest, PrebuiltLibunwind) { - std::filesystem::path prebuilt_runtimes_path = test_runfiles_->Rlocation( - "carbon/toolchain/driver/prebuilt_runtimes_tree"); + std::filesystem::path prebuilt_runtimes_path = + test_runfiles_->Rlocation("carbon/toolchain/install/runtimes"); TestLibunwind(prebuilt_runtimes_path / "libunwind/lib/libunwind.a"); } TEST_F(ClangRuntimesTest, PrebuiltLibcxx) { - std::filesystem::path prebuilt_runtimes_path = test_runfiles_->Rlocation( - "carbon/toolchain/driver/prebuilt_runtimes_tree"); + std::filesystem::path prebuilt_runtimes_path = + test_runfiles_->Rlocation("carbon/toolchain/install/runtimes"); TestLibcxx(prebuilt_runtimes_path / "libcxx/lib/libc++.a"); } diff --git a/toolchain/driver/testdata/compile/fail_clang_args.carbon b/toolchain/driver/testdata/compile/fail_clang_args.carbon index 545d8ac39bbb..ccb53a34e5a7 100644 --- a/toolchain/driver/testdata/compile/fail_clang_args.carbon +++ b/toolchain/driver/testdata/compile/fail_clang_args.carbon @@ -12,7 +12,7 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/compile/fail_clang_args.carbon // CHECK:STDERR: {{.*}}clang version {{.*}} -// CHECK:STDERR: InstalledDir: {{.*}}/toolchain/install/prefix/lib/carbon/llvm/bin -// CHECK:STDERR: "{{.*}}/toolchain/install/prefix/lib/carbon/llvm/bin/clang" "-cc1" {{.*}}"-triple" "x86_64-unknown-linux-gnu" {{.*}}"-fsyntax-only" {{.*}} "-resource-dir" {{.*}} "-Wall" "-Wextra" "-Wuninitialized" "-Wno-all" {{.*}} +// CHECK:STDERR: InstalledDir: {{.*}}/toolchain/install/llvm/bin +// CHECK:STDERR: "{{.*}}/toolchain/install/llvm/bin/clang" "-cc1" {{.*}}"-triple" "x86_64-unknown-linux-gnu" {{.*}}"-fsyntax-only" {{.*}} "-resource-dir" {{.*}} "-Wall" "-Wextra" "-Wuninitialized" "-Wno-all" {{.*}} // --- foo.carbon diff --git a/toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_size.carbon b/toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_size.carbon index 97627e450559..dc29c4e938d2 100644 --- a/toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_size.carbon +++ b/toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_size.carbon @@ -11,6 +11,6 @@ // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_size.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_size.carbon -// CHECK:STDERR: "{{.*}}/toolchain/install/prefix/lib/carbon/llvm/bin/clang" "-cc1" {{.*}} "-Oz" {{.*}} +// CHECK:STDERR: "{{.*}}/toolchain/install/llvm/bin/clang" "-cc1" {{.*}} "-Oz" {{.*}} // --- fail_foo.carbon diff --git a/toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_speed.carbon b/toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_speed.carbon index ce53b1c5f19c..f925fcf08f20 100644 --- a/toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_speed.carbon +++ b/toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_speed.carbon @@ -11,6 +11,6 @@ // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_speed.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_speed.carbon -// CHECK:STDERR: "{{.*}}/toolchain/install/prefix/lib/carbon/llvm/bin/clang" "-cc1" {{.*}} "-O3" {{.*}} +// CHECK:STDERR: "{{.*}}/toolchain/install/llvm/bin/clang" "-cc1" {{.*}} "-O3" {{.*}} // --- fail_foo.carbon diff --git a/toolchain/driver/testdata/compile/optimize/fail_clang_no_optimize_arg.carbon b/toolchain/driver/testdata/compile/optimize/fail_clang_no_optimize_arg.carbon index aebbb4726bff..bde5a545fead 100644 --- a/toolchain/driver/testdata/compile/optimize/fail_clang_no_optimize_arg.carbon +++ b/toolchain/driver/testdata/compile/optimize/fail_clang_no_optimize_arg.carbon @@ -11,6 +11,6 @@ // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/driver/testdata/compile/optimize/fail_clang_no_optimize_arg.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/compile/optimize/fail_clang_no_optimize_arg.carbon -// CHECK:STDERR: "{{.*}}/toolchain/install/prefix/lib/carbon/llvm/bin/clang" "-cc1" {{.*}} "-O1" {{.*}} +// CHECK:STDERR: "{{.*}}/toolchain/install/llvm/bin/clang" "-cc1" {{.*}} "-O1" {{.*}} // --- fail_foo.carbon diff --git a/toolchain/driver/testdata/compile/optimize/fail_clang_override_optimize_arg.carbon b/toolchain/driver/testdata/compile/optimize/fail_clang_override_optimize_arg.carbon index a729b4622827..d6606eb16bcc 100644 --- a/toolchain/driver/testdata/compile/optimize/fail_clang_override_optimize_arg.carbon +++ b/toolchain/driver/testdata/compile/optimize/fail_clang_override_optimize_arg.carbon @@ -11,6 +11,6 @@ // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/driver/testdata/compile/optimize/fail_clang_override_optimize_arg.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/compile/optimize/fail_clang_override_optimize_arg.carbon -// CHECK:STDERR: "{{.*}}/toolchain/install/prefix/lib/carbon/llvm/bin/clang" "-cc1" {{.*}} "-O1" {{.*}} +// CHECK:STDERR: "{{.*}}/toolchain/install/llvm/bin/clang" "-cc1" {{.*}} "-O1" {{.*}} // --- fail_foo.carbon diff --git a/toolchain/driver/testdata/fail_config.carbon b/toolchain/driver/testdata/fail_config.carbon index 01954e7d2bff..ea20ee3408d8 100644 --- a/toolchain/driver/testdata/fail_config.carbon +++ b/toolchain/driver/testdata/fail_config.carbon @@ -21,9 +21,9 @@ // tests. // // CHECK:STDOUT: CLANG_INCLUDE_DIRS: -// CHECK:STDOUT: {{.*}}/toolchain/install/prefix/lib/carbon/llvm/lib/clang/{{[^/]+}}/include -// CHECK:STDOUT: CLANG_RESOURCE_DIR: {{.*}}/toolchain/install/prefix/lib/carbon/llvm/lib/clang/{{[^/]+}} +// CHECK:STDOUT: {{.*}}/toolchain/install/llvm/lib/clang/{{[^/]+}}/include +// CHECK:STDOUT: CLANG_RESOURCE_DIR: {{.*}}/toolchain/install/llvm/lib/clang/{{[^/]+}} // CHECK:STDOUT: CLANG_SYSROOT: / -// CHECK:STDOUT: INSTALL_ROOT: {{.*}}/toolchain/install/prefix/lib/carbon/ -// CHECK:STDOUT: LLVM_BINDIR: {{.*}}/toolchain/install/prefix/lib/carbon/llvm/bin +// CHECK:STDOUT: INSTALL_ROOT: {{.*}}/toolchain/install/ +// CHECK:STDOUT: LLVM_BINDIR: {{.*}}/toolchain/install/llvm/bin // CHECK:STDOUT: VERSION: {{[0-9]+}}.{{[0-9]+}}.{{[0-9]+}}-{{.+}} diff --git a/toolchain/install/BUILD b/toolchain/install/BUILD index a5b2d0e4c58f..0ad5e615b481 100644 --- a/toolchain/install/BUILD +++ b/toolchain/install/BUILD @@ -1,28 +1,71 @@ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# Build rules supporting the install data tree for the Carbon toolchain. +# +# This populates a synthetic Carbon toolchain installation rooted at this +# package in the output tree. +# +# We use a `toolchain_files` rule organize the files in in the installation and +# handle cases where we need to symlink existing files or build outputs +# into the correct location. +# +# This package also includes the logic for building Carbon's runtimes, and Bazel +# `cc_toolchain`s for using the installation as a C++ toolchain in Bazel. This +# allows multi-stage bootstrapping to occur entirely within Bazel. Note that the +# specific stage will be in a _configuration specific_ output tree, but always +# rooted under the install package. +# +# Last but ont least, we provide rules to build packages of the installation in +# conventional layouts for directly extracting and using a binary build. The +# packaged installation includes custom Bazel files to handle a pre-built and +# installed toolchain in the same way as these handle the just-built toolchain. load( "@llvm-project//:vars.bzl", "LLVM_VERSION_MAJOR", ) +load( + "@llvm-project//compiler-rt:compiler-rt.bzl", + "builtins_copts", + "crt_copts", +) +load("@llvm-project//libcxx:libcxx_library.bzl", "libcxx_and_abi_copts") +load("@llvm-project//libunwind:libunwind_library.bzl", "libunwind_copts") load("@rules_python//python:defs.bzl", "py_test") load("//bazel/cc_rules:defs.bzl", "cc_binary", "cc_library", "cc_test") +load( + "//bazel/cc_toolchains:carbon_cc_toolchain_config.bzl", + "carbon_cc_toolchain_suite", + "set_platform_filegroup", +) load("//bazel/manifest:defs.bzl", "manifest") -load("//toolchain/base:llvm_tools.bzl", "LLVM_MAIN_TOOLS", "LLVM_TOOL_ALIASES") load( "//toolchain/base:runtimes_build_info.bzl", "generate_runtimes_build_vars", ) -load("install_filegroups.bzl", "install_filegroup", "install_symlink", "install_target", "make_install_filegroups") +load("//toolchain/runtimes:carbon_runtimes.bzl", "carbon_runtimes") +load("bazel/make_include_copts.bzl", "make_include_copts") +load( + "install_filegroups.bzl", + "filtered_toolchain_files", + "toolchain_files", + "toolchain_llvm_binaries", + "toolchain_pkg_filegroup", +) load("pkg_helpers.bzl", "pkg_naming_variables", "pkg_tar_and_test") package(default_visibility = ["//visibility:public"]) -# Build rules supporting the install data tree for the Carbon toolchain. -# -# This populates a synthetic Carbon toolchain installation under the -# `prefix` directory. For details on its layout, see `install_dirs` below. +constraint_setting( + name = "runtimes_build", +) + +constraint_value( + name = "is_runtimes_build", + constraint_setting = ":runtimes_build", +) cc_library( name = "busybox_info", @@ -71,162 +114,258 @@ cc_binary( ], ) -clang_aliases = [ - "clang", - "clang++", - "clang-cl", - "clang-cpp", -] +toolchain_files( + name = "install_marker", + srcs = ["carbon_install.txt"], +) -# TODO: Add remaining aliases of LLD for Windows and WASM when we have support -# for them wired up through the busybox. -lld_aliases = [ - "ld.lld", - "ld64.lld", -] +toolchain_llvm_binaries( + name = "llvm_bins", + carbon_binary = ":carbon-busybox", +) -llvm_binaries = clang_aliases + lld_aliases + [ - tool.bin_name - for tool in LLVM_MAIN_TOOLS.values() -] + [ - "llvm-" + alias - for (_, aliases) in LLVM_TOOL_ALIASES.items() - for alias in aliases -] +toolchain_files( + name = "core", + srcs = ["//core:prelude"], + prefix = "core/", +) + +toolchain_files( + name = "clang_builtin_hdrs", + srcs = ["@llvm-project//clang:builtin_headers_gen"], + prefix = "llvm/lib/clang/{0}/include/".format(LLVM_VERSION_MAJOR), + remove_prefix = "staging/include/", +) + +toolchain_files( + name = "fuzzer_runtime_hdrs", + srcs = ["@llvm-project//compiler-rt:fuzzer_installed_hdrs"], + prefix = "llvm/lib/clang/{0}/include/".format(LLVM_VERSION_MAJOR), + remove_prefix = "include/", +) + +toolchain_files( + name = "profile_runtime_hdrs", + srcs = ["@llvm-project//compiler-rt:profile_installed_hdrs"], + prefix = "llvm/lib/clang/{0}/include/".format(LLVM_VERSION_MAJOR), + remove_prefix = "include/", +) + +toolchain_files( + name = "sanitizer_runtime_hdrs", + srcs = ["@llvm-project//compiler-rt:sanitizer_installed_hdrs"], + prefix = "llvm/lib/clang/{0}/include/".format(LLVM_VERSION_MAJOR), + remove_prefix = "include/", +) + +filegroup( + name = "clang_hdrs", + srcs = [ + ":clang_builtin_hdrs", + ":fuzzer_runtime_hdrs", + ":profile_runtime_hdrs", + ":sanitizer_runtime_hdrs", + ], +) + +filtered_toolchain_files( + name = "builtins_srcs", + prefix = "runtimes/builtins/", + remove_prefix = "lib/builtins/", + srcs_groups = [ + "@llvm-project//compiler-rt:builtins_aarch64_srcs", + "@llvm-project//compiler-rt:builtins_aarch64_textual_srcs", + "@llvm-project//compiler-rt:builtins_crtbegin_src", + "@llvm-project//compiler-rt:builtins_crtend_src", + "@llvm-project//compiler-rt:builtins_i386_srcs", + "@llvm-project//compiler-rt:builtins_i386_textual_srcs", + "@llvm-project//compiler-rt:builtins_x86_64_srcs", + "@llvm-project//compiler-rt:builtins_x86_64_textual_srcs", + ], +) + +toolchain_files( + name = "libcxx_basic_hdrs", + srcs = ["@llvm-project//libcxx:libcxx_hdrs"], + prefix = "runtimes/libcxx/include/", + remove_prefix = "include/", +) + +toolchain_files( + name = "libcxx_gen_hdrs", + srcs = ["//toolchain/runtimes:libcxx_gen_files"], + prefix = "runtimes/libcxx/include/", + remove_prefix = "staging_libcxx/include/", +) + +filegroup( + name = "libcxx_hdrs", + srcs = [ + ":libcxx_basic_hdrs", + ":libcxx_gen_hdrs", + ], +) + +filtered_toolchain_files( + name = "libcxx_srcs", + prefix = "runtimes/libcxx/src/", + remove_prefix = "src/", + srcs_groups = [ + "@llvm-project//libcxx:libcxx_linux_srcs", + "@llvm-project//libcxx:libcxx_macos_srcs", + "@llvm-project//libcxx:libcxx_win32_srcs", + ], +) + +toolchain_files( + name = "libcxxabi_hdrs", + srcs = ["@llvm-project//libcxxabi:libcxxabi_hdrs"], + prefix = "runtimes/libcxxabi/include/", + remove_prefix = "include/", +) + +toolchain_files( + name = "libcxxabi_srcs", + srcs = ["@llvm-project//libcxxabi:libcxxabi_srcs"], + prefix = "runtimes/libcxxabi/src/", + remove_prefix = "src/", +) + +toolchain_files( + name = "libcxxabi_textual_srcs", + srcs = ["@llvm-project//libcxxabi:libcxxabi_textual_srcs"], + prefix = "runtimes/libcxxabi/src/", + remove_prefix = "src/", +) + +toolchain_files( + name = "libc_internal_libcxx_hdrs", + srcs = ["@llvm-project//libc:libcxx_shared_headers_hdrs"], + prefix = "runtimes/libc/internal/", + #remove_prefix = "src", +) + +toolchain_files( + name = "libunwind_hdrs", + srcs = ["@llvm-project//libunwind:libunwind_hdrs"], + prefix = "runtimes/libunwind/include/", + remove_prefix = "include/", +) + +toolchain_files( + name = "libunwind_srcs", + srcs = ["@llvm-project//libunwind:libunwind_srcs"], + prefix = "runtimes/libunwind/src/", + remove_prefix = "src/", +) + +filegroup( + name = "cc_toolchain_all_files", + srcs = [ + ":carbon-busybox", + ":clang_hdrs", + ":core", + ":install_marker", + ":libcxx_hdrs", + ":libcxxabi_hdrs", + ":libunwind_hdrs", + ":llvm_bins", + ], +) + +toolchain_files( + name = "bazel_common_srcs", + srcs = ["//bazel/cc_toolchains:installed_cc_toolchain_starlark"], + prefix = "bazel/", +) + +toolchain_files( + name = "bazel_install_srcs", + srcs = [ + "bazel/carbon_clang_variables.bzl", + "bazel/carbon_detected_variables.tpl.bzl", + "bazel/carbon_toolchain.bzl", + "bazel/make_include_copts.bzl", + ], +) + +toolchain_files( + name = "carbon_runtimes_installed", + srcs = ["//toolchain/runtimes:carbon_runtimes.bzl"], + prefix = "bazel/", +) + +toolchain_files( + name = "bazel_build_and_module", + srcs = [ + "bazel/empty.BUILD", + "bazel/install.BUILD", + "bazel/install.MODULE.bazel", + ], + remove_prefix = "bazel/", + renames = { + "empty.BUILD": "bazel/BUILD.bazel", + "install.BUILD": "BUILD.bazel", + "install.MODULE.bazel": "MODULE.bazel", + }, +) # Generate a Starlark file with all the build variables needed for our runtimes. generate_runtimes_build_vars( - name = "staging_bazel/runtimes_build_vars.bzl", + name = "bazel/runtimes_build_vars.bzl", ) -# Given a CMake-style install prefix[1], the hierarchy looks like: -# -# - prefix/bin: Binaries intended for direct use. -# - prefix/lib/carbon: Private data and files. -# - prefix/lib/carbon/core: The `Core` package files. -# - prefix/lib/carbon/llvm/bin: LLVM binaries. -# -# This will be how installs are provided on Unix-y platforms, and is loosely -# based on the FHS (Filesystem Hierarchy Standard). See the CMake install prefix -# documentation[1] for more details. -# -# [1]: https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html -install_dirs = { - "bin": [ - install_symlink( - "carbon", - "../lib/carbon/carbon-busybox", - is_driver = True, - ), - ], - "lib/carbon": [ - install_target("MODULE.bazel", "bazel/install.MODULE.bazel"), - install_target("BUILD", "bazel/install.BUILD"), - install_target("carbon_install.txt", "carbon_install.txt"), - install_target( - "install_digest.txt", - ":install_digest.txt", - executable = False, - is_digest = True, - is_driver = True, - ), - install_target( - "carbon-busybox", - ":carbon-busybox", - executable = True, - is_driver = True, - ), - install_filegroup("bazel", "//bazel/cc_toolchains:installed_cc_toolchain_starlark"), - # TODO: Consider if we want to keep `core` here or group it with - # runtimes. It is a bit of both -- standard library, and runtimes. - install_filegroup("core", "//core:prelude"), - ], - "lib/carbon/bazel": [ - install_target("carbon_cc_toolchain_config.bzl", "bazel/carbon_cc_toolchain_config.bzl"), - install_target("carbon_detected_variables.tpl.bzl", "bazel/carbon_detected_variables.tpl.bzl"), - install_target("carbon_runtimes.bzl", "bazel/carbon_runtimes.bzl"), - install_target("carbon_toolchain.bzl", "bazel/carbon_toolchain.bzl"), - install_target("make_include_copts.bzl", "bazel/make_include_copts.bzl"), - install_target("runtimes_build_vars.bzl", "staging_bazel/runtimes_build_vars.bzl"), - install_target("BUILD", "bazel/empty.BUILD"), - ], - "lib/carbon/llvm/bin": [install_symlink( - name, - "../../carbon-busybox", - is_driver = True, - ) for name in llvm_binaries], - "lib/carbon/runtimes": [ - install_filegroup( - "builtins", - "//toolchain/runtimes:clang_builtins_runtimes", - remove_prefix = "lib/builtins/", - ), - install_filegroup("libcxx", "//toolchain/runtimes:libcxx"), - install_filegroup("libcxxabi", "//toolchain/runtimes:libcxxabi"), - install_filegroup("libunwind", "//toolchain/runtimes:libunwind"), - install_target("BUILD", "bazel/runtimes.BUILD"), - ], - "lib/carbon/runtimes/libc": [ - install_filegroup("internal", "//toolchain/runtimes:libc_internal"), - ], - "lib/carbon/runtimes/libcxx": [ - install_filegroup( - "include", - "//toolchain/runtimes:libcxx_gen_files", - remove_prefix = "staging_libcxx/include/", - ), - ], - "lib/carbon/llvm/lib/clang/" + LLVM_VERSION_MAJOR: [ - install_target("BUILD", "bazel/clang_resource_dir.BUILD"), - install_filegroup( - "include", - "@llvm-project//clang:builtin_headers_gen", - label = "installed_clang_headers", - remove_prefix = "staging/include/", - ), - ], - "lib/carbon/llvm/lib/clang/" + LLVM_VERSION_MAJOR + "/include": [ - install_filegroup( - "fuzzer", - "@llvm-project//compiler-rt:fuzzer_installed_hdrs", - remove_prefix = "include/fuzzer/", - ), - install_filegroup( - "profile", - "@llvm-project//compiler-rt:profile_installed_hdrs", - remove_prefix = "include/profile/", - ), - install_filegroup( - "sanitizer", - "@llvm-project//compiler-rt:sanitizer_installed_hdrs", - remove_prefix = "include/sanitizer/", - ), - ], -} +filegroup( + name = "installed_bazel_files", + srcs = [ + ":bazel_build_and_module", + ":bazel_common_srcs", + ":bazel_install_srcs", + ":carbon_runtimes_installed", -make_install_filegroups( - name = "install_data", - install_dirs = install_dirs, - no_digest_name = "install_data.no_digest", - no_driver_name = "install_data.no_driver", - pkg_name = "pkg_data", - prefix = "prefix", + # Note that we have to put this here and not in one of the + # `toolchain_files` because it is a generated file. + "bazel/runtimes_build_vars.bzl", + ], ) -py_test( - name = "llvm_symlinks_test", - size = "small", - srcs = ["llvm_symlinks_test.py"], - data = [ - ":install_data", - "//toolchain/driver:prebuilt_runtimes", +filegroup( + name = "all_data_files", + srcs = [ + ":builtins_all_srcs", + ":clang_hdrs", + ":core", + ":install_marker", + ":installed_bazel_files", + ":libc_internal_libcxx_hdrs", + ":libcxx_all_srcs", + ":libcxx_hdrs", + ":libcxxabi_hdrs", + ":libcxxabi_srcs", + ":libunwind_hdrs", + ":libunwind_srcs", + ], +) + +# A list of clang's installed builtin header files. +# This is consumed by //toolchain/testing:file_test. +manifest( + name = "clang_headers_manifest.txt", + srcs = [":clang_hdrs"], + strip_package_dir = True, +) + +filegroup( + name = "all_digest_files", + srcs = [ + ":all_data_files", + ":carbon-busybox", ], - deps = ["@bazel_tools//tools/python/runfiles"], ) manifest( - name = "install_data_manifest.txt", - srcs = [":install_data"], + name = "install_digest_manifest.txt", + srcs = [":all_digest_files"], ) cc_binary( @@ -244,35 +383,286 @@ cc_binary( ], ) -manifest( - name = "install_digest_manifest.txt", - srcs = [":install_data.no_digest"], -) - genrule( name = "gen_digest", srcs = [ - ":install_data.no_digest", + ":all_digest_files", "install_digest_manifest.txt", ], - outs = [":install_digest.txt"], + outs = ["install_digest.txt"], cmd = "$(location :make-installation-digest) " + "$(location install_digest_manifest.txt) $@", tools = [":make-installation-digest"], ) -# A list of clang's installed builtin header files. -# This is consumed by //toolchain/testing:file_test. +filegroup( + name = "install_data", + srcs = [ + "install_digest.txt", + ":all_digest_files", + ":llvm_bins", + ], +) + manifest( - name = "clang_headers_manifest.txt", - srcs = [":installed_clang_headers"], - strip_package_dir = True, + name = "install_data_manifest.txt", + srcs = [":install_data"], +) + +cc_library( + name = "builtins_internal", + hdrs_check = "strict", + textual_hdrs = select({ + "@platforms//cpu:aarch64": [":builtins_aarch64_textual_srcs"], + "@platforms//cpu:i386": [":builtins_i386_textual_srcs"], + "@platforms//cpu:x86_64": [":builtins_x86_64_textual_srcs"], + "//conditions:default": [], + }), +) + +cc_library( + name = "builtins", + srcs = select({ + "@platforms//cpu:aarch64": [":builtins_aarch64_srcs"], + "@platforms//cpu:i386": [":builtins_i386_srcs"], + "@platforms//cpu:x86_64": [":builtins_x86_64_srcs"], + "//conditions:default": [], + }), + copts = builtins_copts + make_include_copts([ + "runtimes/builtins", + ]) + [ + # TODO: Remove this once we are building sanitizer runtime libraries. + "-fno-sanitize=all", + ], + hdrs_check = "strict", + target_compatible_with = select({ + "//bazel/cc_toolchains:stage1": [], + "//bazel/cc_toolchains:stage2": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + deps = [":builtins_internal"], +) + +filegroup( + name = "builtins_archive", + srcs = [":builtins"], + output_group = "archive", +) + +cc_library( + name = "libunwind", + srcs = [":libunwind_srcs"], + hdrs = [":libunwind_hdrs"], + copts = libunwind_copts + [ + # We disable all warnings as upstream isn't clean with the common + # warning flags Carbon uses by default. + "-w", + + # TODO: Remove this once we are building sanitizer runtime libraries. + "-fno-sanitize=all", + ], + hdrs_check = "strict", + includes = ["runtimes/libunwind/include"], + linkstatic = 1, + target_compatible_with = select({ + "//bazel/cc_toolchains:stage1": [], + "//bazel/cc_toolchains:stage2": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +filegroup( + name = "libunwind_archive", + srcs = [":libunwind"], + output_group = "archive", +) + +cc_library( + name = "libcxxabi_internal", + hdrs_check = "strict", + target_compatible_with = select({ + "//bazel/cc_toolchains:stage1": [], + "//bazel/cc_toolchains:stage2": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + textual_hdrs = [":libcxxabi_textual_srcs"], +) + +cc_library( + name = "libc_internal_libcxx", + hdrs = [":libc_internal_libcxx_hdrs"], + hdrs_check = "strict", + target_compatible_with = select({ + "//bazel/cc_toolchains:stage1": [], + "//bazel/cc_toolchains:stage2": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +cc_library( + name = "libcxx", + srcs = select({ + "@platforms//os:macos": [":libcxx_macos_srcs"], + "@platforms//os:windows": [":libcxx_win32_srcs"], + "//conditions:default": [":libcxx_linux_srcs"], + }) + [":libcxxabi_srcs"], + hdrs = [ + ":libcxx_hdrs", + ":libcxxabi_hdrs", + ], + copts = libcxx_and_abi_copts + make_include_copts([ + "runtimes/libcxx/src", + "runtimes/libc/internal", + ]) + [ + # We disable all warnings as upstream isn't clean with the common + # warning flags Carbon uses by default. + "-w", + + # TODO: Remove this once we are building sanitizer runtime libraries. + "-fno-sanitize=all", + ], + hdrs_check = "strict", + includes = [ + "runtimes/libcxx/include", + "runtimes/libcxxabi/include", + ], + target_compatible_with = select({ + "//bazel/cc_toolchains:stage1": [], + "//bazel/cc_toolchains:stage2": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + deps = [ + ":libc_internal_libcxx", + ":libcxxabi_internal", + ], +) + +filegroup( + name = "libcxx_archive", + srcs = [":libcxx"], + output_group = "archive", +) + +carbon_runtimes( + name = "runtimes", + builtins_archive = ":builtins_archive", + clang_hdrs = [":clang_hdrs"], + clang_hdrs_prefix = "llvm/lib/clang/{0}/include/".format(LLVM_VERSION_MAJOR), + crt_copts = crt_copts + [ + # Disable all sanitizers for CRT objects. + "-fno-sanitize=all", + ], + crtbegin_src = select({ + "@platforms//os:linux": ":builtins_crtbegin_src", + "//conditions:default": None, + }), + crtend_src = select({ + "@platforms//os:linux": ":builtins_crtend_src", + "//conditions:default": None, + }), + darwin_os_suffix = select({ + # TODO: Add support for tvOS, watchOS, and iOS variants with the + # relevant Bazel constraints. + ":is_macos_arm64": "osx", + ":is_macos_x86_64": "osx", + "//conditions:default": None, + }), + libcxx_archive = ":libcxx_archive", + libunwind_archive = ":libunwind_archive", + target_compatible_with = select({ + "//bazel/cc_toolchains:stage1": [], + "//bazel/cc_toolchains:stage2": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + target_triple = select({ + # TODO: Add other triples (and if needed, constraints) so that we can + # build the correct Clang resource-dir structure for each. + ":is_freebsd_x86_64": "x86_64-unknown-freebsd", + ":is_linux_aarch64": "aarch64-unknown-linux-gnu", + ":is_linux_x86_64": "x86_64-unknown-linux-gnu", + + # Note that Darwin OSes are handled by the `darwin_os_suffix` attribute. + "//conditions:default": None, + }), +) + +platforms = { + "freebsd": ["x86_64"], + "linux": [ + "aarch64", + "x86_64", + ], + "macos": [ + "arm64", + "x86_64", + ], +} + +[ + config_setting( + name = "is_{0}_{1}".format(os, cpu), + constraint_values = [ + "@platforms//os:" + os, + "@platforms//cpu:" + cpu, + ], + ) + for os, cpus in platforms.items() + for cpu in cpus +] + +carbon_cc_toolchain_suite( + name = "carbon_stage1", + all_hdrs = [ + ":clang_hdrs", + ":libunwind_hdrs", + ":libcxx_hdrs", + ":libcxxabi_hdrs", + ], + base_files = [ + ":install_marker", + ":carbon-busybox", + ":llvm_bins", + ], + build_stage = "//bazel/cc_toolchains:stage1", + clang_hdrs = [":clang_hdrs"], + platforms = platforms, + runtimes = ":runtimes", +) + +carbon_cc_toolchain_suite( + name = "carbon_stage2", + all_hdrs = [ + ":clang_hdrs", + ":libunwind_hdrs", + ":libcxx_hdrs", + ":libcxxabi_hdrs", + ], + base_files = [ + ":install_marker", + ":carbon-busybox", + ":llvm_bins", + ], + base_stage = "//bazel/cc_toolchains:stage1", + build_stage = "//bazel/cc_toolchains:stage2", + clang_hdrs = [":clang_hdrs"], + platforms = platforms, + runtimes = ":runtimes", + tags = ["manual"], ) pkg_naming_variables( name = "packaging_variables", ) +toolchain_pkg_filegroup( + name = "stage1_pkg_data", + srcs = [ + "install_digest.txt", + ":all_data_files", + ], + carbon_busybox = ":carbon-busybox", +) + # We build both a compressed and uncompressed tar file with the same code here. # This lets us use the tar file in testing as it is fast to create, but ship the # compressed version as a release. @@ -280,7 +670,7 @@ pkg_naming_variables( # For manual tests, the tar rules are `carbon_toolchain_tar_rule` and # `carbon_toolchain_tar_gz_rule`. pkg_tar_and_test( - srcs = [":pkg_data"], + srcs = [":stage1_pkg_data"], install_data_manifest = ":install_data_manifest.txt", name_base = "carbon_toolchain", package_dir = "carbon_toolchain-$(version)", @@ -288,3 +678,62 @@ pkg_tar_and_test( package_variables = ":packaging_variables", stamp = -1, # Allow `--stamp` builds to produce file timestamps. ) + +set_platform_filegroup( + name = "stage2_pkg_files", + srcs = [ + ":all_data_files", + ":gen_digest", + ], + platform = select({ + ":is_{}_{}".format(os, cpu): ":carbon_stage2_base_{}_{}_platform".format(os, cpu) + for os, cpus in platforms.items() + for cpu in cpus + }), + tags = ["manual"], +) + +set_platform_filegroup( + name = "stage2_busybox", + srcs = [":carbon-busybox"], + platform = select({ + ":is_{}_{}".format(os, cpu): ":carbon_stage2_base_{}_{}_platform".format(os, cpu) + for os, cpus in platforms.items() + for cpu in cpus + }), + tags = ["manual"], +) + +toolchain_pkg_filegroup( + name = "stage2_pkg_data", + srcs = [":stage2_pkg_files"], + carbon_busybox = ":stage2_busybox", + tags = ["manual"], +) + +pkg_tar_and_test( + srcs = [":stage2_pkg_data"], + install_data_manifest = ":install_data_manifest.txt", + name_base = "carbon_bootstrapped_toolchain", + package_dir = "carbon_toolchain-$(version)", + package_file_name_base = "carbon_bootstrapped_toolchain-$(version)", + package_variables = ":packaging_variables", + stamp = -1, # Allow `--stamp` builds to produce file timestamps. + tags = ["manual"], +) + +filegroup( + name = "built_runtimes", + srcs = [":carbon_stage1_runtimes"], +) + +py_test( + name = "llvm_symlinks_test", + size = "small", + srcs = ["llvm_symlinks_test.py"], + data = [ + ":built_runtimes", + ":install_data", + ], + deps = ["@bazel_tools//tools/python/runfiles"], +) diff --git a/toolchain/install/bazel/carbon_cc_toolchain_config.bzl b/toolchain/install/bazel/carbon_cc_toolchain_config.bzl deleted file mode 100644 index 51e1f0e5e8e6..000000000000 --- a/toolchain/install/bazel/carbon_cc_toolchain_config.bzl +++ /dev/null @@ -1,319 +0,0 @@ -# Part of the Carbon Language project, under the Apache License v2.0 with LLVM -# Exceptions. See /LICENSE for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -"""Starlark cc_toolchain configuration rules for using the Carbon toolchain""" - -load( - "@carbon_toolchain_config//:carbon_detected_variables.bzl", - "clang_include_dirs", - "clang_sysroot", -) -load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") -load( - "@rules_cc//cc:cc_toolchain_config_lib.bzl", - "action_config", - "flag_group", - "flag_set", - "tool", -) -load( - "@rules_cc//cc:defs.bzl", - "CcToolchainConfigInfo", - "cc_toolchain", -) -load("@rules_cc//cc/common:cc_common.bzl", "cc_common") -load("//bazel:runtimes_build_vars.bzl", "llvm_version_major") -load( - ":cc_toolchain_actions.bzl", - "all_c_compile_actions", - "all_cpp_compile_actions", - "all_link_actions", -) -load(":cc_toolchain_features.bzl", "clang_cc_toolchain_features") -load( - ":cc_toolchain_tools.bzl", - "llvm_tool_paths", -) - -def _make_action_configs(runtimes_path = None): - runtimes_flag = "--no-build-runtimes" - if runtimes_path: - runtimes_flag = "--prebuilt-runtimes={0}".format(runtimes_path) - - return [ - action_config( - action_name = name, - enabled = True, - tools = [tool(path = "llvm/bin/clang")], - ) - for name in all_c_compile_actions - ] + [ - action_config( - action_name = name, - enabled = True, - tools = [tool(path = "llvm/bin/clang++")], - ) - for name in all_cpp_compile_actions - ] + [ - action_config( - action_name = name, - enabled = True, - tools = [tool(path = "carbon-busybox")], - flag_sets = [flag_set(flag_groups = [flag_group(flags = [ - runtimes_flag, - "link", - # We want to allow Bazel to intermingle linked object files and - # Clang-spelled link flags. The first `--` starts the list of - # initial object files by ending flags to the `link` subcommand, - # and the second `--` switches to Clang-spelled flags. - "--", - "--", - ])])], - ) - for name in all_link_actions - ] + [ - action_config( - action_name = name, - enabled = True, - tools = [tool(path = "llvm/bin/llvm-ar")], - ) - for name in [ACTION_NAMES.cpp_link_static_library] - ] + [ - action_config( - action_name = name, - enabled = True, - tools = [tool(path = "llvm/bin/llvm-strip")], - ) - for name in [ACTION_NAMES.strip] - ] - -def _carbon_cc_toolchain_config_impl(ctx): - # Hard code the the repository-relative path of the LLVM (and Clang) - # binaries as it is a fixed aspect of the install structure. - llvm_bindir = "llvm/bin" - - # Only use a sysroot if a non-trivial one is set in Carbon's config. - builtin_sysroot = None - if clang_sysroot != "None" and clang_sysroot != "/": - builtin_sysroot = clang_sysroot - - runtimes_path = None - if ctx.attr.runtimes: - for f in ctx.files.runtimes: - if f.basename == "runtimes_root": - runtimes_path = f.dirname - break - if not runtimes_path: - fail("Unable to compute the runtimes path for: {0}".format( - ctx.attr.runtimes, - )) - - identifier = "carbon-toolchain-{0}-{1}".format( - ctx.attr.target_cpu, - ctx.attr.target_os, - ) - return cc_common.create_cc_toolchain_config_info( - ctx = ctx, - features = clang_cc_toolchain_features( - target_os = ctx.attr.target_os, - target_cpu = ctx.attr.target_cpu, - ), - action_configs = _make_action_configs(runtimes_path), - cxx_builtin_include_directories = clang_include_dirs, - builtin_sysroot = builtin_sysroot, - - # This configuration only supports local non-cross builds so derive - # everything from the target CPU selected. - toolchain_identifier = identifier, - - # This is used to expose a "flag" that `config_setting` rules can use to - # determine if the compiler is Clang. - compiler = "clang", - - # We do have to pass in our tool paths. - tool_paths = llvm_tool_paths(llvm_bindir), - ) - -carbon_cc_toolchain_config = rule( - implementation = _carbon_cc_toolchain_config_impl, - attrs = { - "runtimes": attr.label(mandatory = False), - "target_cpu": attr.string(mandatory = True), - "target_os": attr.string(mandatory = True), - }, - provides = [CcToolchainConfigInfo], -) - -def _runtimes_transition_impl(_, attr): - # Adjust the platform to the runtimes platform across the transition. - return {"//command_line_option:platforms": [str(attr.runtimes_platform)]} - -runtimes_transition = transition( - inputs = [], - outputs = ["//command_line_option:platforms"], - implementation = _runtimes_transition_impl, -) - -def _runtimes_filegroup_impl(ctx): - return [DefaultInfo(files = depset(ctx.files.srcs))] - -runtimes_filegroup = rule( - implementation = _runtimes_filegroup_impl, - attrs = { - # The platform to use when building the runtimes. - "runtimes_platform": attr.label(mandatory = True), - - # Mark that our dependencies are built through a transition. - "srcs": attr.label_list(mandatory = True, cfg = runtimes_transition), - - # Enable transitions in this rule. - "_allowlist_function_transition": attr.label( - default = "@bazel_tools//tools/allowlists/function_transition_allowlist", - ), - }, -) - -def carbon_cc_toolchain_suite(name, platforms): - """Create a toolchain suite that uses the local Clang/LLVM install. - - Args: - name: The name of the toolchain suite to produce. - platforms: An array of (os, cpu) pairs to support in the toolchain. - """ - - # Our base filegroup for the toolchain just includes the busybox and the - # LLVM symlinks. Importantly, this _doesn't_ include the install digest that - # would cause cache misses as-if every file in the toolchain were an input - # to every action. - native.filegroup( - name = name + "_base_files", - srcs = ["carbon-busybox", "carbon_install.txt"] + native.glob([ - "llvm/bin/*", - ]), - ) - - # We also need a compile-specific filegroup for use when _building_ - # runtimes. This needs to include the dedicated Clang headers, but we - # require the runtimes themselves to provide the relevant headers and - # dependencies. - native.filegroup( - name = name + "_runtimes_compile_files", - srcs = [ - ":" + name + "_base_files", - "//llvm/lib/clang/{0}:clang_hdrs".format(llvm_version_major), - ], - ) - - # We can build a single common compile filegroup regardless of CPU and OS. - # This needs to include all the headers of the runtimes, but those are - # architecture independent. - native.filegroup( - name = name + "_compile_files", - srcs = [ - ":" + name + "_runtimes_compile_files", - "//runtimes:libunwind_hdrs", - "//runtimes:libcxx_hdrs", - ], - ) - - # Create the actual toolchains for each OS and CPU. - for (os, cpu) in platforms: - platform_name = "{0}_{1}_{2}".format(name, os, cpu) - platform_constraints = [ - "@platforms//os:" + os, - "@platforms//cpu:" + cpu, - ] - - # First, configure a platform and toolchain for building runtimes. This - # toolchain will only have the Clang headers and no runtime libraries - # included. - native.platform( - name = platform_name + "_runtimes_platform", - constraint_values = [":is_runtimes_build"] + platform_constraints, - ) - carbon_cc_toolchain_config( - name = platform_name + "_runtimes_toolchain_config", - target_cpu = cpu, - target_os = os, - ) - cc_toolchain( - name = platform_name + "_runtimes_cc_toolchain", - all_files = ":" + name + "_runtimes_compile_files", - ar_files = ":" + name + "_base_files", - as_files = ":" + name + "_runtimes_compile_files", - compiler_files = ":" + name + "_runtimes_compile_files", - dwp_files = ":" + name + "_base_files", - linker_files = ":" + name + "_base_files", - objcopy_files = ":" + name + "_base_files", - strip_files = ":" + name + "_base_files", - toolchain_config = ":" + platform_name + "_runtimes_toolchain_config", - toolchain_identifier = platform_name + "_runtimes", - ) - native.toolchain( - name = platform_name + "_runtimes_toolchain", - exec_compatible_with = platform_constraints, - target_compatible_with = [":is_runtimes_build"] + platform_constraints, - toolchain = platform_name + "_runtimes_cc_toolchain", - toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", - ) - - # Now we can use the runtimes platform to build the runtimes on-demand. - runtimes_filegroup( - name = platform_name + "_runtimes", - srcs = ["//runtimes:carbon_runtimes"], - runtimes_platform = ":" + platform_name + "_runtimes_platform", - ) - - # Finally, we can build the main platform and toolchain. - native.platform( - name = platform_name + "_platform", - constraint_values = platform_constraints, - ) - - # Build the main config with runtimes passed in. This allows the - # configuration to use these built runtimes where needed. - carbon_cc_toolchain_config( - name = platform_name + "_toolchain_config", - target_cpu = cpu, - target_os = os, - runtimes = ":" + platform_name + "_runtimes", - ) - - # We also include the runtimes in the linker files. We have to do this - # in addition to the config parameter as the config can't carry the - # actual dependency. - native.filegroup( - name = platform_name + "_linker_files", - srcs = [ - ":" + name + "_base_files", - ":" + platform_name + "_runtimes", - ], - ) - native.filegroup( - name = platform_name + "_all_files", - srcs = [ - ":" + name + "_compile_files", - ":" + platform_name + "_linker_files", - ], - ) - cc_toolchain( - name = platform_name + "_cc_toolchain", - all_files = ":" + platform_name + "_all_files", - ar_files = ":" + name + "_base_files", - as_files = ":" + name + "_compile_files", - compiler_files = ":" + name + "_compile_files", - dwp_files = ":" + platform_name + "_linker_files", - linker_files = ":" + platform_name + "_linker_files", - objcopy_files = ":" + name + "_base_files", - strip_files = ":" + name + "_base_files", - toolchain_config = ":" + platform_name + "_toolchain_config", - toolchain_identifier = platform_name, - ) - native.toolchain( - name = platform_name + "_toolchain", - exec_compatible_with = platform_constraints, - target_compatible_with = platform_constraints, - toolchain = platform_name + "_cc_toolchain", - toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", - ) diff --git a/toolchain/install/bazel/carbon_clang_variables.bzl b/toolchain/install/bazel/carbon_clang_variables.bzl new file mode 100644 index 000000000000..1fd168bacad2 --- /dev/null +++ b/toolchain/install/bazel/carbon_clang_variables.bzl @@ -0,0 +1,16 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""A Starlark file exporting detected Carbon toolchain configuration variables.""" + +load( + "@carbon_toolchain_config//:carbon_detected_variables.bzl", + _clang_include_dirs = "clang_include_dirs", + _clang_resource_dir = "clang_resource_dir", + _clang_sysroot = "clang_sysroot", +) + +clang_include_dirs = _clang_include_dirs +clang_resource_dir = _clang_resource_dir +clang_sysroot = _clang_sysroot diff --git a/toolchain/install/bazel/carbon_detected_variables.tpl.bzl b/toolchain/install/bazel/carbon_detected_variables.tpl.bzl index a3f02258d98d..56b6f27b9c9f 100644 --- a/toolchain/install/bazel/carbon_detected_variables.tpl.bzl +++ b/toolchain/install/bazel/carbon_detected_variables.tpl.bzl @@ -9,4 +9,5 @@ values, for example using an invocation of `carbon config`. """ clang_include_dirs = CLANG_INCLUDE_DIRS +clang_resource_dir = "CLANG_RESOURCE_DIR" clang_sysroot = "CLANG_SYSROOT" diff --git a/toolchain/install/bazel/clang_resource_dir.BUILD b/toolchain/install/bazel/clang_resource_dir.BUILD deleted file mode 100644 index 0808d06c75cd..000000000000 --- a/toolchain/install/bazel/clang_resource_dir.BUILD +++ /dev/null @@ -1,10 +0,0 @@ -# Part of the Carbon Language project, under the Apache License v2.0 with LLVM -# Exceptions. See /LICENSE for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -package(default_visibility = ["//visibility:public"]) - -filegroup( - name = "clang_hdrs", - srcs = glob(["include/**"]), -) diff --git a/toolchain/install/bazel/install.BUILD b/toolchain/install/bazel/install.BUILD index 83eb108b88a4..2fc3bcdd42e8 100644 --- a/toolchain/install/bazel/install.BUILD +++ b/toolchain/install/bazel/install.BUILD @@ -2,7 +2,42 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +load("@bazel_skylib//rules:common_settings.bzl", "string_list_setting") +load("@rules_cc//cc:defs.bzl", "cc_library") load("//bazel:carbon_cc_toolchain_config.bzl", "carbon_cc_toolchain_suite") +load("//bazel:carbon_runtimes.bzl", "carbon_runtimes") +load("//bazel:make_include_copts.bzl", "make_include_copts") +load( + "//bazel:runtimes_build_vars.bzl", + "builtins_aarch64_srcs", + "builtins_aarch64_textual_srcs", + "builtins_copts", + "builtins_i386_srcs", + "builtins_i386_textual_srcs", + "builtins_x86_64_srcs", + "builtins_x86_64_textual_srcs", + "crt_copts", + "crtbegin_src", + "crtend_src", + "libc_internal_libcxx_hdrs", + "libcxx_copts", + "libcxx_hdrs", + "libcxx_linux_srcs", + "libcxx_macos_srcs", + "libcxx_win32_srcs", + "libcxxabi_hdrs", + "libcxxabi_srcs", + "libcxxabi_textual_srcs", + "libunwind_copts", + "libunwind_hdrs", + "libunwind_srcs", + "llvm_version_major", +) + +_libcxx_hdrs = libcxx_hdrs + [ + "runtimes/libcxx/include/__config_site", + "runtimes/libcxx/include/__assertion_handler", +] package(default_visibility = ["//visibility:public"]) @@ -13,13 +48,240 @@ constraint_value( constraint_setting = ":runtimes_build", ) -carbon_cc_toolchain_suite( - name = "carbon", - platforms = [ - ("linux", "aarch64"), - ("linux", "x86_64"), - ("freebsd", "x86_64"), - ("macos", "arm64"), - ("macos", "x86_64"), +filegroup( + name = "llvm_bins", + srcs = glob(["llvm/bin/*"]), +) + +filegroup( + name = "clang_hdrs", + srcs = glob(["llvm/lib/clang/{0}/include/*".format(llvm_version_major)]), +) + +filegroup( + name = "libcxx_hdrs", + srcs = _libcxx_hdrs + libcxxabi_hdrs, +) + +filegroup( + name = "libunwind_hdrs", + srcs = libunwind_hdrs, +) + +cc_library( + name = "builtins_internal", + hdrs_check = "strict", + textual_hdrs = select({ + "@platforms//cpu:aarch64": builtins_aarch64_textual_srcs, + "@platforms//cpu:i386": builtins_i386_textual_srcs, + "@platforms//cpu:x86_64": builtins_x86_64_textual_srcs, + "//conditions:default": [], + }), +) + +cc_library( + name = "builtins", + srcs = select({ + "@platforms//cpu:aarch64": builtins_aarch64_srcs, + "@platforms//cpu:i386": builtins_i386_srcs, + "@platforms//cpu:x86_64": builtins_x86_64_srcs, + "//conditions:default": [], + }), + copts = builtins_copts + make_include_copts([ + "runtimes/builtins", + ]), + hdrs_check = "strict", + target_compatible_with = select({ + ":is_runtimes_build": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + deps = [":builtins_internal"], +) + +filegroup( + name = "builtins_archive", + srcs = [":builtins"], + output_group = "archive", +) + +cc_library( + name = "libunwind", + srcs = libunwind_srcs, + hdrs = [":libunwind_hdrs"], + copts = libunwind_copts + [ + # We disable all warnings as upstream isn't clean with the common + # warning flags Carbon uses by default. + "-w", + ], + hdrs_check = "strict", + includes = ["runtimes/libunwind/include"], + linkstatic = 1, + target_compatible_with = select({ + ":is_runtimes_build": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +filegroup( + name = "libunwind_archive", + srcs = [":libunwind"], + output_group = "archive", +) + +cc_library( + name = "libcxxabi_internal", + hdrs_check = "strict", + target_compatible_with = select({ + ":is_runtimes_build": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + textual_hdrs = libcxxabi_textual_srcs, +) + +cc_library( + name = "libc_internal_libcxx", + hdrs = libc_internal_libcxx_hdrs, + hdrs_check = "strict", + target_compatible_with = select({ + ":is_runtimes_build": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +cc_library( + name = "libcxx", + srcs = select({ + "@platforms//os:macos": libcxx_macos_srcs, + "@platforms//os:windows": libcxx_win32_srcs, + "//conditions:default": libcxx_linux_srcs, + }) + libcxxabi_srcs, + hdrs = [":libcxx_hdrs"], + copts = libcxx_copts + make_include_copts([ + "runtimes/libcxx/src", + "runtimes/libc/internal", + ]) + [ + # We disable all warnings as upstream isn't clean with the common + # warning flags Carbon uses by default. + "-w", + ], + hdrs_check = "strict", + includes = [ + "runtimes/libcxx/include", + "runtimes/libcxxabi/include", + ], + target_compatible_with = select({ + ":is_runtimes_build": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + deps = [ + ":libc_internal_libcxx", + ":libcxxabi_internal", ], ) + +filegroup( + name = "libcxx_archive", + srcs = [":libcxx"], + output_group = "archive", +) + +carbon_runtimes( + name = "runtimes", + builtins_archive = ":builtins_archive", + clang_hdrs = [":clang_hdrs"], + clang_hdrs_prefix = "llvm/lib/clang/{0}/include/".format(llvm_version_major), + crt_copts = crt_copts, + crtbegin_src = select({ + "@platforms//os:linux": crtbegin_src, + "//conditions:default": None, + }), + crtend_src = select({ + "@platforms//os:linux": crtend_src, + "//conditions:default": None, + }), + darwin_os_suffix = select({ + # TODO: Add support for tvOS, watchOS, and iOS variants with the + # relevant Bazel constraints. + ":is_macos_arm64": "osx", + ":is_macos_x86_64": "osx", + "//conditions:default": None, + }), + libcxx_archive = ":libcxx_archive", + libunwind_archive = ":libunwind_archive", + target_compatible_with = select({ + ":is_runtimes_build": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + target_triple = select({ + # TODO: Add other triples (and if needed, constraints) so that we can + # build the correct Clang resource-dir structure for each. + ":is_freebsd_x86_64": "x86_64-unknown-freebsd", + ":is_linux_aarch64": "aarch64-unknown-linux-gnu", + ":is_linux_x86_64": "x86_64-unknown-linux-gnu", + + # Note that Darwin OSes are handled by the `darwin_os_suffix` attribute. + "//conditions:default": None, + }), +) + +filegroup( + name = "carbon_install_digest_file", + srcs = ["install_digest.txt"], +) + +filegroup( + name = "carbon_install_marker_file", + srcs = ["carbon_install.txt"], +) + +filegroup( + name = "carbon_busybox_file", + srcs = ["carbon-busybox"], +) + +string_list_setting( + name = "original_platforms", + build_setting_default = [], +) + +platforms = { + "freebsd": ["x86_64"], + "linux": [ + "aarch64", + "x86_64", + ], + "macos": [ + "arm64", + "x86_64", + ], +} + +[ + config_setting( + name = "is_{0}_{1}".format(os, cpu), + constraint_values = [ + "@platforms//os:" + os, + "@platforms//cpu:" + cpu, + ], + ) + for os, cpus in platforms.items() + for cpu in cpus +] + +carbon_cc_toolchain_suite( + name = "carbon", + all_hdrs = [ + ":clang_hdrs", + ":libunwind_hdrs", + ":libcxx_hdrs", + ], + base_files = [ + ":carbon_install_digest_file", + ":carbon_install_marker_file", + ":carbon_busybox_file", + ":llvm_bins", + ], + clang_hdrs = [":clang_hdrs"], + platforms = platforms, + runtimes = ":runtimes", +) diff --git a/toolchain/install/bazel/install.MODULE.bazel b/toolchain/install/bazel/install.MODULE.bazel index d3993827a8bf..51cc4045dc09 100644 --- a/toolchain/install/bazel/install.MODULE.bazel +++ b/toolchain/install/bazel/install.MODULE.bazel @@ -13,8 +13,9 @@ more detailed examples, see the Carbon project examples in examples/bazel module(name = "carbon_toolchain") -bazel_dep(name = "rules_cc", version = "0.1.4") +bazel_dep(name = "rules_cc", version = "0.2.17") bazel_dep(name = "platforms", version = "1.0.0") +bazel_dep(name = "bazel_skylib", version = "1.9.0") carbon_toolchain_config = use_extension( "//bazel:carbon_toolchain.bzl", diff --git a/toolchain/install/bazel/runtimes.BUILD b/toolchain/install/bazel/runtimes.BUILD deleted file mode 100644 index b71f2c5792d0..000000000000 --- a/toolchain/install/bazel/runtimes.BUILD +++ /dev/null @@ -1,196 +0,0 @@ -# Part of the Carbon Language project, under the Apache License v2.0 with LLVM -# Exceptions. See /LICENSE for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -load("@rules_cc//cc:defs.bzl", "cc_library") -load("//bazel:carbon_runtimes.bzl", "carbon_runtimes") -load("//bazel:make_include_copts.bzl", "make_include_copts") -load( - "//bazel:runtimes_build_vars.bzl", - "builtins_aarch64_srcs", - "builtins_aarch64_textual_srcs", - "builtins_copts", - "builtins_i386_srcs", - "builtins_i386_textual_srcs", - "builtins_x86_64_srcs", - "builtins_x86_64_textual_srcs", - "crt_copts", - "crtbegin_src", - "crtend_src", - "libc_internal_libcxx_hdrs", - "libcxx_copts", - "libcxx_hdrs", - "libcxx_linux_srcs", - "libcxx_macos_srcs", - "libcxx_win32_srcs", - "libcxxabi_hdrs", - "libcxxabi_srcs", - "libcxxabi_textual_srcs", - "libunwind_copts", - "libunwind_hdrs", - "libunwind_srcs", - "llvm_version_major", -) - -package(default_visibility = ["//visibility:public"]) - -# Clang looks in a target-triple subdirectory of its resource directory for the -# builtins and CRT files. Triples capture a large range of settings and aren't -# even consistent structurally between platforms, so we expect to have roughly -# one setting per supported target triple. By convention, we name them after the -# target triple. -# -# TODO: Add constraints and other settings so we can select the correct target -# triple for different `libc`s. -# -# TODO: Add other OS and CPU triples. -config_setting( - name = "x86_64-unknown-linux-gnu", - constraint_values = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], -) - -config_setting( - name = "aarch64-unknown-linux-gnu", - constraint_values = [ - "@platforms//os:linux", - "@platforms//cpu:aarch64", - ], -) - -cc_library( - name = "builtins_internal", - hdrs_check = "strict", - textual_hdrs = select({ - "@platforms//cpu:aarch64": builtins_aarch64_textual_srcs, - "@platforms//cpu:i386": builtins_i386_textual_srcs, - "@platforms//cpu:x86_64": builtins_x86_64_textual_srcs, - "//conditions:default": [], - }), -) - -cc_library( - name = "builtins", - srcs = select({ - "@platforms//cpu:aarch64": builtins_aarch64_srcs, - "@platforms//cpu:i386": builtins_i386_srcs, - "@platforms//cpu:x86_64": builtins_x86_64_srcs, - "//conditions:default": [], - }), - copts = builtins_copts + make_include_copts([ - "builtins", - ]), - hdrs_check = "strict", - deps = [":builtins_internal"], -) - -filegroup( - name = "builtins_archive", - srcs = [":builtins"], - output_group = "archive", -) - -filegroup( - name = "libunwind_hdrs", - srcs = libunwind_hdrs, -) - -cc_library( - name = "libunwind", - srcs = libunwind_srcs, - hdrs = [":libunwind_hdrs"], - copts = libunwind_copts + [ - # We disable all warnings as upstream isn't clean with the common - # warning flags Carbon uses by default. - "-w", - ], - hdrs_check = "strict", - includes = ["libunwind/include"], - linkstatic = 1, -) - -filegroup( - name = "libunwind_archive", - srcs = [":libunwind"], - output_group = "archive", -) - -cc_library( - name = "libcxxabi_internal", - hdrs_check = "strict", - textual_hdrs = libcxxabi_textual_srcs, -) - -cc_library( - name = "libc_internal_libcxx", - hdrs = libc_internal_libcxx_hdrs, - hdrs_check = "strict", -) - -filegroup( - name = "libcxx_hdrs", - srcs = [ - "libcxx/include/__assertion_handler", - "libcxx/include/__config_site", - ] + libcxx_hdrs + libcxxabi_hdrs, -) - -cc_library( - name = "libcxx", - srcs = select({ - "@platforms//os:macos": libcxx_macos_srcs, - "@platforms//os:windows": libcxx_win32_srcs, - "//conditions:default": libcxx_linux_srcs, - }) + libcxxabi_srcs, - hdrs = [":libcxx_hdrs"], - copts = libcxx_copts + make_include_copts([ - "libcxx/src", - "libc/internal", - ]) + [ - # We disable all warnings as upstream isn't clean with the common - # warning flags Carbon uses by default. - "-w", - ], - hdrs_check = "strict", - includes = [ - "libcxx/include", - "libcxxabi/include", - ], - linkstatic = 1, - deps = [ - ":libc_internal_libcxx", - ":libcxxabi_internal", - ], -) - -filegroup( - name = "libcxx_archive", - srcs = [":libcxx"], - output_group = "archive", -) - -carbon_runtimes( - name = "carbon_runtimes", - builtins_archive = ":builtins_archive", - clang_hdrs = [ - "//llvm/lib/clang/{0}:clang_hdrs".format(llvm_version_major), - ], - crt_copts = crt_copts, - crtbegin_src = select({ - "@platforms//os:linux": crtbegin_src, - "//conditions:default": None, - }), - crtend_src = select({ - "@platforms//os:linux": crtend_src, - "//conditions:default": None, - }), - libcxx_archive = ":libcxx_archive", - libunwind_archive = ":libunwind_archive", - target_triple = select({ - ":aarch64-unknown-linux-gnu": "aarch64-unknown-linux-gnu", - ":x86_64-unknown-linux-gnu": "x86_64-unknown-linux-gnu", - "//conditions:default": "", - }), -) diff --git a/toolchain/install/busybox_info.cpp b/toolchain/install/busybox_info.cpp index ed183e05156e..74a0ea038f61 100644 --- a/toolchain/install/busybox_info.cpp +++ b/toolchain/install/busybox_info.cpp @@ -55,18 +55,6 @@ auto GetBusyboxInfo(const char* argv0) -> ErrorOr { // Now search through any symlinks to locate the installed busybox binary. while (true) { if (info.bin_path.filename() == "carbon-busybox") { - // Check for bazel structure. For example, this makes work: - // /bin/sh -c "exec -a carbon ./bazel-bin/toolchain/carbon" - // /bin/sh -c "exec -a llvm-symbolizer ./bazel-bin/toolchain/carbon" - // - // This will never occur in a "bin" subdirectory, so doesn't need to be - // handled in the other return path. - std::string busybox_path = info.bin_path.parent_path().string() + - "/prefix/lib/carbon/carbon-busybox"; - if (auto access = Filesystem::Cwd().Access(busybox_path); - access.ok() && *access) { - info.bin_path = busybox_path; - } return info; } @@ -77,8 +65,8 @@ auto GetBusyboxInfo(const char* argv0) -> ErrorOr { // output tree. // // We break this into two cases we need to handle: - // - Carbon's CLI will be: `/bin/carbon` - // - Other tools will be: `/lib/carbon//bin/` + // - An install using the Unix-style FHS layout: `/bin/carbon` + // - Tools within the Carbon install root: `//bin/` // // We also check that the current path is within a `bin` directory to // provide best-effort checking for accidentally walking up from symlinks diff --git a/toolchain/install/install_filegroups.bzl b/toolchain/install/install_filegroups.bzl index 1337a134246a..075fba9a26d5 100644 --- a/toolchain/install/install_filegroups.bzl +++ b/toolchain/install/install_filegroups.bzl @@ -5,162 +5,195 @@ """Rules for constructing install information.""" load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_filegroup", "pkg_files", "pkg_mklink", "strip_prefix") -load("symlink_helpers.bzl", "symlink_file", "symlink_filegroup") +load("//toolchain/base:llvm_tools.bzl", "LLVM_MAIN_TOOLS", "LLVM_TOOL_ALIASES") -def install_filegroup(name, filegroup_target, remove_prefix = "", label = None): - """Adds a filegroup for install. +_clang_aliases = [ + "clang", + "clang++", + "clang-cl", + "clang-cpp", +] - Used in the `install_dirs` dict. +# TODO: Add remaining aliases of LLD for Windows and WASM when we have support +# for them wired up through the busybox. +_lld_aliases = [ + "ld.lld", + "ld64.lld", +] - Args: - name: The base directory for the filegroup. - filegroup_target: The bazel filegroup target to install. - remove_prefix: A prefix to remove from the name of each source file when - determining the name of the corresponding installed file. - label: A custom label to assign to the filegroup containing the - installed files. +_llvm_binaries = _clang_aliases + _lld_aliases + [ + tool.bin_name + for tool in LLVM_MAIN_TOOLS.values() +] + [ + "llvm-" + alias + for (_, aliases) in LLVM_TOOL_ALIASES.items() + for alias in aliases +] + +def _toolchain_llvm_binaries_impl(ctx): + outputs = [] + for bin in _llvm_binaries: + out = ctx.actions.declare_file(ctx.attr.prefix + "llvm/bin/" + bin) + ctx.actions.symlink( + output = out, + target_file = ctx.files.carbon_binary[0], + ) + outputs.append(out) + + return [DefaultInfo(files = depset(direct = outputs))] + +toolchain_llvm_binaries = rule( + doc = "Creates symlinks for LLVM binaries pointing to a single Carbon binary.", + implementation = _toolchain_llvm_binaries_impl, + attrs = { + "carbon_binary": attr.label( + allow_single_file = True, + #executable = True, + mandatory = True, + #cfg = None, + ), + "prefix": attr.string(default = ""), + }, +) + +def _removeprefix_or_fail(s, prefix): + if prefix == "": + return s + new_s = s.removeprefix(prefix) + if new_s == s: + fail("Unable to remove prefix '{0}' from '{1}'".format(prefix, s)) + return new_s + +def _get_pkg_relative_path(file): + path = file.path + if file.root.path != "": + path = _removeprefix_or_fail(path, file.root.path + "/") + if file.owner.workspace_root != "": + path = _removeprefix_or_fail(path, file.owner.workspace_root + "/") + return _removeprefix_or_fail(path, file.owner.package + "/") + +def _toolchain_files_impl(ctx): + prefix = ctx.attr.prefix + outputs = [] + for src in ctx.files.srcs: + rel_path = _get_pkg_relative_path(src) + rel_path = _removeprefix_or_fail(rel_path, ctx.attr.remove_prefix) + if rel_path in ctx.attr.renames: + rel_path = ctx.attr.renames[rel_path] + out = ctx.actions.declare_file("{0}{1}".format(prefix, rel_path)) + ctx.actions.symlink(output = out, target_file = src) + outputs.append(out) + + return [DefaultInfo(files = depset(outputs))] + +toolchain_files = rule( + doc = "Arranges files into a directory structure by symlinking them with prefix removal and renames.", + implementation = _toolchain_files_impl, + attrs = { + "prefix": attr.string(default = ""), + "remove_prefix": attr.string(default = ""), + "renames": attr.string_dict(default = {}), + "srcs": attr.label_list(allow_files = True), + }, +) + +def _filtered_files_impl(ctx): + include_set = set() + for filter_src in ctx.files.filter_to_srcs: + rel_path = _get_pkg_relative_path(filter_src) + include_set.add(_removeprefix_or_fail(rel_path, ctx.attr.filter_to_srcs_prefix)) + + outputs = [] + for src in ctx.files.srcs: + rel_path = _get_pkg_relative_path(src) + if _removeprefix_or_fail(rel_path, ctx.attr.srcs_prefix) in include_set: + outputs.append(src) + + return [DefaultInfo(files = depset(outputs))] + +filtered_files = rule( + doc = "Filters a set of files based on their relative paths matching another set of files.", + implementation = _filtered_files_impl, + attrs = { + "filter_to_srcs": attr.label_list(), + "filter_to_srcs_prefix": attr.string(default = ""), + "srcs": attr.label_list(), + "srcs_prefix": attr.string(default = ""), + }, +) + +def filtered_toolchain_files(name, prefix, remove_prefix, srcs_groups): + """A collection of filtered toolchain files from overlapping `srcs`.""" + name_base = name.removesuffix("_srcs") + if name_base == "" or name_base == name: + fail("Invalid name for building a set of filtered toolchain files.") + toolchain_files( + name = name_base + "_all_srcs", + srcs = srcs_groups, + prefix = prefix, + remove_prefix = remove_prefix, + ) + + for srcs in srcs_groups: + suffix = srcs.partition(":")[2].removeprefix(name_base) + filtered_files( + name = name_base + suffix, + srcs = [":" + name_base + "_all_srcs"], + srcs_prefix = prefix, + filter_to_srcs = [srcs], + filter_to_srcs_prefix = remove_prefix, + ) + +def toolchain_pkg_filegroup(name, carbon_busybox, srcs, tags = []): + """Given a CMake-style install prefix[1], the hierarchy looks like: + + - prefix/bin: Binaries intended for direct use. + - prefix/lib/carbon: Private data and files. + - prefix/lib/carbon/core: The `Core` package files. + - prefix/lib/carbon/llvm/bin: LLVM binaries. + + This will be how installs are provided on Unix-y platforms, and is loosely + based on the FHS (Filesystem Hierarchy Standard). See the CMake install prefix + documentation[1] for more details. + + [1]: https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html """ - return { - "filegroup": filegroup_target, - "is_digest": False, - "is_driver": False, - "label": label, - "name": name, - "remove_prefix": remove_prefix, - } - -def install_symlink(name, symlink_to, is_driver = False): - """Adds a symlink for install. - - Used in the `install_dirs` dict. - - Args: - name: The filename to use. - symlink_to: A relative path for the symlink. - is_driver: False if it should be included in the `no_driver_name` - filegroup. - """ - return { - "is_digest": False, - "is_driver": is_driver, - "name": name, - "symlink": symlink_to, - } - -def install_target(name, target, executable = False, is_driver = False, is_digest = False): - """Adds a target for install. - - Used in the `install_dirs` dict. - - Args: - name: The filename to use. - target: The bazel target being installed. - executable: True if executable. - is_driver: False if it should be included in the `no_driver_name` - filegroup. - is_digest: False if it should be included in the `no_digest_name` - filegroup. - """ - return { - "executable": executable, - "is_digest": is_digest, - "is_driver": is_driver, - "name": name, - "target": target, - } - -def make_install_filegroups(name, no_digest_name, no_driver_name, pkg_name, install_dirs, prefix): - """Makes filegroups of install data. - - Args: - name: The name of the main filegroup, that contains all install_data. - no_digest_name: The name of a filegroup which excludes the digest. This is - used to compute the digest itself. - no_driver_name: The name of a filegroup which excludes the driver. This is - for the driver to depend on and get other files, without a circular - dependency. - pkg_name: The name of a pkg_filegroup for tar. - install_dirs: A dict of {directory: [install_* rules]}. This is used to - structure files to be installed. - prefix: A prefix for files in the native (non-pkg) filegroups. - """ - all_srcs = [] - no_driver_srcs = [] - no_digest_srcs = [] pkg_srcs = [] - for dir, entries in install_dirs.items(): - for entry in entries: - path = "{0}/{1}".format(dir, entry["name"]) + # Separately handle the busybox so that we can set its attributes. + pkg_files( + name = name + "_busybox", + srcs = [carbon_busybox], + prefix = "lib/carbon", + attributes = pkg_attributes(mode = "0755"), + tags = tags, + ) + pkg_srcs.append(":" + name + "_busybox") - prefixed_path = "{0}/{1}".format(prefix, path) - all_srcs.append(prefixed_path) - if not entry["is_driver"]: - no_driver_srcs.append(prefixed_path) - if not entry["is_digest"]: - no_digest_srcs.append(prefixed_path) + pkg_files( + name = name + "_srcs", + srcs = srcs, + prefix = "lib/carbon", + strip_prefix = strip_prefix.from_pkg(), + tags = tags, + ) + pkg_srcs.append(":" + name + "_srcs") - pkg_label = entry.get("label") or path + ".pkg" - pkg_srcs.append(pkg_label) + for bin in _llvm_binaries: + pkg_mklink( + name = name + "_llvm_symlink_" + bin, + link_name = "lib/carbon/llvm/bin/" + bin, + target = "../../carbon-busybox", + tags = tags, + ) + pkg_srcs.append(":" + name + "_llvm_symlink_" + bin) - if "target" in entry: - if entry["executable"]: - symlink_file( - name = prefixed_path, - symlink_binary = entry["target"], - ) - mode = "0755" - else: - symlink_file( - name = prefixed_path, - symlink_label = entry["target"], - ) - mode = "0644" - pkg_files( - name = pkg_label, - srcs = [entry["target"]], - attributes = pkg_attributes(mode = mode), - renames = {entry["target"]: path}, - ) - elif "filegroup" in entry: - symlink_filegroup( - name = prefixed_path, - out_prefix = prefixed_path, - srcs = [entry["filegroup"]], - remove_prefix = entry["remove_prefix"], - ) - pkg_files( - name = pkg_label, - srcs = [prefixed_path], - strip_prefix = strip_prefix.from_pkg(prefix), - ) - elif "symlink" in entry: - symlink_to = "{0}/{1}/{2}".format(prefix, dir, entry["symlink"]) + pkg_mklink( + name = name + "_bin_symlink", + link_name = "bin/carbon", + target = "../lib/carbon/carbon-busybox", + tags = tags, + ) + pkg_srcs.append(":" + name + "_bin_symlink") - # For bazel, we need to resolve relative symlinks. - if "../" in symlink_to: - parts = symlink_to.split("/") - result = [] - for part in parts: - if part == "..": - result = result[:-1] - else: - result.append(part) - symlink_to = "/".join(result) - symlink_file( - name = prefixed_path, - symlink_binary = symlink_to, - ) - - # For the distributed package, we retain relative symlinks. - pkg_mklink( - name = pkg_label, - link_name = path, - target = entry["symlink"], - ) - else: - fail("Unrecognized structure: {0}".format(entry)) - native.filegroup(name = name, srcs = all_srcs) - native.filegroup(name = no_driver_name, srcs = no_driver_srcs) - native.filegroup(name = no_digest_name, srcs = no_digest_srcs) - pkg_filegroup(name = pkg_name, srcs = pkg_srcs) + pkg_filegroup(name = name, srcs = pkg_srcs, tags = tags) diff --git a/toolchain/install/llvm_symlinks_test.py b/toolchain/install/llvm_symlinks_test.py index 542e860809c0..dd94beae56ec 100644 --- a/toolchain/install/llvm_symlinks_test.py +++ b/toolchain/install/llvm_symlinks_test.py @@ -20,13 +20,13 @@ from bazel_tools.tools.python.runfiles import runfiles class LLVMSymlinksTest(unittest.TestCase): def setUp(self) -> None: # The install root is adjacent to the test script - self.install_root = Path(sys.argv[0]).parent / "prefix" + self.install_root = Path(sys.argv[0]).parent self.tmpdir = Path(os.environ["TEST_TMPDIR"]) self.test_o_file = self.tmpdir / "test.o" self.test_o_file.touch() self.runfiles = runfiles.Create() self.prebuilt_runtimes = self.runfiles.Rlocation( - "carbon/toolchain/driver/prebuilt_runtimes_tree" + "carbon/toolchain/install/runtimes" ) def get_link_cmd(self, clang: Path) -> list[str | Path]: @@ -58,7 +58,7 @@ class LLVMSymlinksTest(unittest.TestCase): # developing Carbon, and encode all the different platform results in the # test expectations. def test_clang(self) -> None: - bin = self.install_root / "lib/carbon/llvm/bin/clang" + bin = self.install_root / "llvm/bin/clang" # Most errors are caught by ensuring the command succeeds. run = subprocess.run( self.get_link_cmd(bin), check=True, capture_output=True, text=True @@ -71,7 +71,7 @@ class LLVMSymlinksTest(unittest.TestCase): # Note that we can't test `clang` vs. `clang++` portably. See the comment on # `test_clang` for details. def test_clangplusplus(self) -> None: - bin = self.install_root / "lib/carbon/llvm/bin/clang++" + bin = self.install_root / "llvm/bin/clang++" run = subprocess.run( self.get_link_cmd(bin), check=True, capture_output=True, text=True ) @@ -81,7 +81,7 @@ class LLVMSymlinksTest(unittest.TestCase): self.assertRegex(run.stderr, r'"-lc\+\+"') def test_clang_cl(self) -> None: - bin = self.install_root / "lib/carbon/llvm/bin/clang-cl" + bin = self.install_root / "llvm/bin/clang-cl" run = subprocess.run( # Use the `cl.exe`-specific help flag to test the mode. [bin, "/?"], @@ -104,7 +104,7 @@ class LLVMSymlinksTest(unittest.TestCase): # Run the preprocessor using a CPP-specific command line reading from # the test file and writing to stdout. We define a macro that we'll # check is expanded. - bin = self.install_root / "lib/carbon/llvm/bin/clang-cpp" + bin = self.install_root / "llvm/bin/clang-cpp" try: run = subprocess.run( [bin, "-D", "TEST=SUCCESS", text_file, "-"], diff --git a/toolchain/install/pkg_helpers.bzl b/toolchain/install/pkg_helpers.bzl index 8e1c0badc3f0..6de721091885 100644 --- a/toolchain/install/pkg_helpers.bzl +++ b/toolchain/install/pkg_helpers.bzl @@ -25,7 +25,7 @@ pkg_naming_variables = rule( attrs = VERSION_ATTRS, ) -def pkg_tar_and_test(name_base, package_file_name_base, install_data_manifest, **kwargs): +def pkg_tar_and_test(name_base, package_file_name_base, install_data_manifest, tags = [], **kwargs): """Create a `pkg_tar` and a test for both `.tar` and `.tar.gz` extensions. Args: @@ -37,6 +37,8 @@ def pkg_tar_and_test(name_base, package_file_name_base, install_data_manifest, * extensions will be appended after a `.`. install_data_manifest: The install data manifest file to compare with. + tags: + Tags to apply to the generated rules and tests. **kwargs: Passed to `pkg_tar` for all the rest of its attributes. """ @@ -48,7 +50,7 @@ def pkg_tar_and_test(name_base, package_file_name_base, install_data_manifest, * extension = file_ext, package_file_name = package_file_name_base + "." + file_ext, # The compressed tar is slow, exclude building and testing that. - tags = ["manual"] if file_ext == "tar.gz" else [], + tags = tags + (["manual"] if file_ext == "tar.gz" else []), **kwargs ) @@ -63,5 +65,5 @@ def pkg_tar_and_test(name_base, package_file_name_base, install_data_manifest, * }, main = "toolchain_tar_test.py", # The compressed tar is slow, exclude building and testing that. - tags = ["manual"] if file_ext == "tar.gz" else [], + tags = tags + (["manual"] if file_ext == "tar.gz" else []), ) diff --git a/toolchain/install/toolchain_tar_test.py b/toolchain/install/toolchain_tar_test.py index 5a9fffe12d54..bdcc5154cbd0 100644 --- a/toolchain/install/toolchain_tar_test.py +++ b/toolchain/install/toolchain_tar_test.py @@ -22,15 +22,28 @@ class ToolchainTarTest(unittest.TestCase): # Gather install data files. with open(install_data_manifest) as manifest: - # Remove everything up to and including `prefix`. + # Remove everything up to and including the package path + # `toolchain/install`. install_files = set( [ - re.sub("^.*/prefix/", "", entry.strip()) + re.sub("^.*/toolchain/install/", "", entry.strip()) for entry in manifest.readlines() ] ) self.assertTrue(install_files, f"`{install_data_manifest}` is empty.") + # Gather tar files. + with tarfile.open(tar_file) as tar: + # Remove the first path component. + tar_files = set( + [ + str(Path(*Path(tarinfo.name).parts[1:])) + for tarinfo in tar + if not tarinfo.isdir() + ] + ) + self.assertTrue(install_files, f"`{install_data_manifest}` is empty.") + # Gather tar files. with tarfile.open(tar_file) as tar: # Remove the first path component. @@ -43,6 +56,16 @@ class ToolchainTarTest(unittest.TestCase): ) self.assertTrue(tar_files, f"`{tar_file}` is empty.") + # Check that the `carbon` symlink is in the tar file. + self.assertIn("bin/carbon", tar_files) + tar_files.remove("bin/carbon") + + # Remove the `lib/carbon` prefix which should be on every other file. + tar_files = set( + [re.sub("^lib/carbon/", "", entry.strip()) for entry in tar_files] + ) + + # The install files and the tar files should now be identical. self.assertSetEqual(install_files, tar_files) diff --git a/toolchain/runtimes/BUILD b/toolchain/runtimes/BUILD index da8d9ee789f0..e1dbdb447cd2 100644 --- a/toolchain/runtimes/BUILD +++ b/toolchain/runtimes/BUILD @@ -13,6 +13,8 @@ load("configure_cmake_file.bzl", "configure_cmake_file") package(default_visibility = ["//visibility:public"]) +exports_files(["carbon_runtimes.bzl"]) + # Collect the runtime sources that are collectively installed into the # `builtins` directory. filegroup( diff --git a/toolchain/runtimes/carbon_runtimes.bzl b/toolchain/runtimes/carbon_runtimes.bzl new file mode 100644 index 000000000000..cc00737ebe0b --- /dev/null +++ b/toolchain/runtimes/carbon_runtimes.bzl @@ -0,0 +1,159 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Starlark rules for building a Carbon runtimes tree. + +TODO: Currently, this produces a complete, static Carbon runtimes tree that +mirrors the exact style of runtimes tree the Carbon toolchain would build on its +own. However, it would be preferable to preserve the builtins, libc++, and +libunwind `cc_library` rules as "normal" library rules (if behind a transition) +and automatically depend on them. This would allow things like LTO and such to +include these. However, this requires support in `@rules_cc` for this kind of +dependency to be added. +""" + +load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load("@rules_cc//cc/common:cc_common.bzl", "cc_common") + +def _build_crt_file(ctx, cc_toolchain, feature_configuration, crt_file): + _, compilation_outputs = cc_common.compile( + name = ctx.label.name + ".compile_" + crt_file.basename, + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + srcs = [crt_file], + user_compile_flags = ctx.attr.crt_copts, + ) + + # Extract the PIC object file and make sure we built one. + obj = compilation_outputs.pic_objects[0] + if not obj: + fail("The toolchain failed to produce a PIC object file. Ensure your " + + "toolchain supports PIC.") + + return obj + +def _removeprefix_or_fail(s, prefix): + new_s = s.removeprefix(prefix) + if new_s == s: + fail("Unable to remove prefix '{0}' from '{1}'".format(prefix, s)) + return new_s + +def _carbon_runtimes_impl(ctx): + outputs = [] + prefix = ctx.attr.name + + # Create a marker file in the runtimes root first. We'll use this to locate + # the runtimes for the toolchain. + root_out = ctx.actions.declare_file("{0}/runtimes_root".format(prefix)) + ctx.actions.write(output = root_out, content = "") + outputs.append(root_out) + + # Setup the C++ toolchain and configuration. We also force the `pic` feature + # to be enabled for these actions as we always want PIC generated code -- + # this avoids the need to build two versions of the runtimes and doesn't + # create problems with modern code generation when linking statically. This + # also simplifies extracting the outputs as we only need to look at + # `pic_objects`. + cc_toolchain = find_cpp_toolchain(ctx) + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features + ["pic"], + unsupported_features = ctx.disabled_features, + ) + + builtins_lib_path = "clang_resource_dir/lib" + builtins_archive_name = "libclang_rt.builtins.a" + + if ctx.attr.target_triple != "": + builtins_lib_path = "clang_resource_dir/lib/{0}".format(ctx.attr.target_triple) + elif ctx.attr.darwin_os_suffix: + builtins_lib_path = "clang_resource_dir/lib/darwin" + builtins_archive_name = "libclang_rt.{0}.a".format(ctx.attr.darwin_os_suffix) + + for filename, src in [ + ("crtbegin", ctx.files.crtbegin_src), + ("crtend", ctx.files.crtend_src), + ]: + if not src: + continue + src = src[0] + crt_obj = _build_crt_file(ctx, cc_toolchain, feature_configuration, src) + crt_out = ctx.actions.declare_file("{0}/{1}/clang_rt.{2}.o".format( + prefix, + builtins_lib_path, + filename, + )) + ctx.actions.symlink(output = crt_out, target_file = crt_obj) + outputs.append(crt_out) + + for runtime_dir, archive_name, archive in [ + (builtins_lib_path, builtins_archive_name, ctx.files.builtins_archive[0]), + ("libcxx/lib", "libc++.a", ctx.files.libcxx_archive[0]), + ("libunwind/lib", "libunwind.a", ctx.files.libunwind_archive[0]), + ]: + runtime_out = ctx.actions.declare_file("{0}/{1}/{2}".format( + prefix, + runtime_dir, + archive_name, + )) + ctx.actions.symlink(output = runtime_out, target_file = archive) + outputs.append(runtime_out) + + for hdr in ctx.files.clang_hdrs: + # Incrementally remove prefixes of the paths to find the `include` + # directory we want to symlink into the output tree. + rel_path = hdr.path + if hdr.root.path != "": + rel_path = _removeprefix_or_fail(rel_path, hdr.root.path + "/") + if hdr.owner.workspace_root != "": + rel_path = _removeprefix_or_fail(rel_path, hdr.owner.workspace_root + "/") + if hdr.owner.package != "": + rel_path = _removeprefix_or_fail(rel_path, hdr.owner.package + "/") + rel_path = _removeprefix_or_fail(rel_path, ctx.attr.clang_hdrs_prefix) + + out_hdr = ctx.actions.declare_file( + "{0}/clang_resource_dir/include/{1}".format(prefix, rel_path), + ) + ctx.actions.symlink(output = out_hdr, target_file = hdr) + outputs.append(out_hdr) + + return [DefaultInfo(files = depset(outputs))] + +carbon_runtimes = rule( + implementation = _carbon_runtimes_impl, + attrs = { + "builtins_archive": attr.label(mandatory = True, allow_files = [".a"]), + "clang_hdrs": attr.label_list(mandatory = True, allow_files = True), + "clang_hdrs_prefix": attr.string(default = "include/"), + "crt_copts": attr.string_list(default = []), + "crtbegin_src": attr.label(allow_files = [".c"]), + "crtend_src": attr.label(allow_files = [".c"]), + "darwin_os_suffix": attr.string(mandatory = False), + "libcxx_archive": attr.label(mandatory = True, allow_files = [".a"]), + "libunwind_archive": attr.label(mandatory = True, allow_files = [".a"]), + "target_triple": attr.string(mandatory = False), + "_cc_toolchain": attr.label( + default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), + ), + }, + toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], + fragments = ["cpp"], + doc = """Builds a Carbon runtimes tree under the `name` directory. + + This rule works to replicate the behavior of the Carbon toolchain building a + runtimes tree directly in Bazel to allow it to fully benefit from Bazel's + orchestration, caching, and even remote execution. + + It produces a runtimes tree customized for the specific target platform, + similar to what the Carbon toolchain does on its own when invoked outside of + Bazel. + + The runtimes tree includes a complete Clang resource-dir, including CRT + begin/end objects, and the builtins library. It also includes a built libc++ + and libunwind library. These are arranged in the standard Carbon runtimes + layout so that they are correctly found by the toolchain. + """, +) diff --git a/toolchain/testing/BUILD b/toolchain/testing/BUILD index 8c6efcd8ce05..4cb3d3235a8c 100644 --- a/toolchain/testing/BUILD +++ b/toolchain/testing/BUILD @@ -62,8 +62,8 @@ file_test( srcs = ["file_test.cpp"], data = [ ":min_prelude", + "//toolchain/install:clang_hdrs", "//toolchain/install:clang_headers_manifest.txt", - "//toolchain/install:installed_clang_headers", ], tests = [":all_testdata"], deps = [