From 6aae31b65b4c2d4e6f4ea5d5c5e3f76acf81c568 Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Fri, 4 Dec 2020 19:26:07 -0800 Subject: [PATCH] Merge LLVM and Bazel infra from the toolchain repository. (#207) The Bazel bits are collected into a directory and given less confusing names (I hope). Other than names, everything is a direct copy from the toolchain repository without any edits. A few files needed to be merged in: - `.gitignore` - `.pre-commit-config.yaml` Subsequent commits will add relevant C++ infrastructure and then the source code itself. --- .bazelrc | 8 + .bazelversion | 1 + .gitignore | 1 + .gitmodules | 11 + .pre-commit-config.yaml | 5 +- BUILD | 3 + WORKSPACE | 48 ++ bazel/cc_toolchains/BUILD | 6 + .../clang_cc_toolchain_config.bzl | 725 ++++++++++++++++++ .../clang_detected_variables.tpl.bzl | 13 + bazel/cc_toolchains/clang_detection.bzl | 169 ++++ bazel/cc_toolchains/clang_toolchain.BUILD | 44 ++ bazel/fuzzing/BUILD | 5 + bazel/fuzzing/rules.bzl | 51 ++ third_party/llvm-bazel | 1 + third_party/update_llvm.sh | 19 + 16 files changed, 1109 insertions(+), 1 deletion(-) create mode 100644 .bazelrc create mode 100644 .bazelversion create mode 100644 .gitmodules create mode 100644 BUILD create mode 100644 WORKSPACE create mode 100644 bazel/cc_toolchains/BUILD create mode 100644 bazel/cc_toolchains/clang_cc_toolchain_config.bzl create mode 100644 bazel/cc_toolchains/clang_detected_variables.tpl.bzl create mode 100644 bazel/cc_toolchains/clang_detection.bzl create mode 100644 bazel/cc_toolchains/clang_toolchain.BUILD create mode 100644 bazel/fuzzing/BUILD create mode 100644 bazel/fuzzing/rules.bzl create mode 160000 third_party/llvm-bazel create mode 100755 third_party/update_llvm.sh diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 000000000000..0af2db32bcff --- /dev/null +++ b/.bazelrc @@ -0,0 +1,8 @@ +# 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 --crosstool_top=@bazel_cc_toolchain +build --host_crosstool_top=@bazel_cc_toolchain + +build:fuzzer --features=fuzzer diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 000000000000..5e52a62d72b0 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +last_green diff --git a/.gitignore b/.gitignore index 3d6a90f79d9b..9c6df4ad8aed 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception **/__pycache__/ +/bazel-* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000000..1df2491d7f50 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,11 @@ +# 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 + +[submodule "third_party/llvm-bazel"] + path = third_party/llvm-bazel + url = https://github.com/google/llvm-bazel.git + fetchRecurseSubmodules = false +[submodule "third_party/llvm-project"] + path = third_party/llvm-project + url = https://github.com/llvm/llvm-project.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b73890b43a6d..0346947a18e3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: 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 - exclude: '^website/(firebase/.firebaserc|jekyll/(Gemfile.lock|theme/.*))$' + exclude: '^(website/(firebase/.firebaserc|jekyll/(Gemfile.lock|theme/.*))|.bazelversion|compile_flags.txt|.*\.def)$' - id: check-google-doc-style - id: markdown-toc - repo: https://github.com/codespell-project/codespell @@ -67,3 +67,6 @@ repos: rev: 3.8.4 hooks: - id: flake8 + +# We fully exclude fuzzer corpus directories as they are generated binary data. +exclude: '^(.*/fuzzer_corpus/.*)$' diff --git a/BUILD b/BUILD new file mode 100644 index 000000000000..0340ab965ad2 --- /dev/null +++ b/BUILD @@ -0,0 +1,3 @@ +# 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 diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 000000000000..14014796d34c --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,48 @@ +# 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 + +workspace(name = "carbon") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +# We want to use LLVM via an external CMake build, so pull in the Bazel +# infrastructure that provides direct CMake interfacing support. +http_archive( + name = "rules_foreign_cc", + strip_prefix = "rules_foreign_cc-master", + url = "https://github.com/bazelbuild/rules_foreign_cc/archive/master.zip", +) + +# Set up necessary dependencies for working with the foreign C++ rules. +load("@rules_foreign_cc//:workspace_definitions.bzl", "rules_foreign_cc_dependencies") + +rules_foreign_cc_dependencies() + +# Detect and configure a Clang and LLVM based toolchain. +load("//bazel/cc_toolchains:clang_detection.bzl", "detect_clang_toolchain") + +detect_clang_toolchain(name = "bazel_cc_toolchain") + +local_repository( + name = "llvm_bazel", + path = "third_party/llvm-bazel/llvm-bazel", +) + +load("@llvm_bazel//:configure.bzl", "llvm_configure") + +llvm_configure( + name = "llvm-project", + src_path = "third_party/llvm-project", + src_workspace = "@carbon//:WORKSPACE", +) + +load("@llvm_bazel//:terminfo.bzl", "llvm_terminfo_system") + +# We require successful detection and use of a system terminfo library. +llvm_terminfo_system(name = "llvm_terminfo") + +load("@llvm_bazel//:zlib.bzl", "llvm_zlib_system") + +# We require successful detection and use of a system zlib library. +llvm_zlib_system(name = "llvm_zlib") diff --git a/bazel/cc_toolchains/BUILD b/bazel/cc_toolchains/BUILD new file mode 100644 index 000000000000..0e9ed617e1dd --- /dev/null +++ b/bazel/cc_toolchains/BUILD @@ -0,0 +1,6 @@ +# 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 + +# Empty `BUILD` file as this package just provides repository rule inputs to +# configure a Clang toolchain. diff --git a/bazel/cc_toolchains/clang_cc_toolchain_config.bzl b/bazel/cc_toolchains/clang_cc_toolchain_config.bzl new file mode 100644 index 000000000000..54c7d2d08276 --- /dev/null +++ b/bazel/cc_toolchains/clang_cc_toolchain_config.bzl @@ -0,0 +1,725 @@ +# 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 cc_toolchain configuration rule""" + +load( + "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", + "action_config", + "feature", + "feature_set", + "flag_group", + "flag_set", + "tool", + "tool_path", + "variable_with_value", + "with_feature_set", +) +load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") +load( + ":clang_detected_variables.bzl", + "clang_include_dirs_list", + "clang_resource_dir", + "llvm_bindir", +) + +all_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, +] + +all_cpp_compile_actions = [ + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, +] + +preprocessor_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, +] + +codegen_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_module_codegen, +] + +all_link_actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, +] + +def _impl(ctx): + tool_paths = [ + tool_path(name = "ar", path = llvm_bindir + "/llvm-ar"), + tool_path(name = "ld", path = llvm_bindir + "/ld.lld"), + tool_path(name = "cpp", path = llvm_bindir + "/clang-cpp"), + tool_path(name = "gcc", path = llvm_bindir + "/clang"), + tool_path(name = "dwp", path = llvm_bindir + "/llvm-dwp"), + tool_path(name = "gcov", path = llvm_bindir + "/llvm-cov"), + tool_path(name = "nm", path = llvm_bindir + "/llvm-nm"), + tool_path(name = "objcopy", path = llvm_bindir + "/llvm-objcopy"), + tool_path(name = "objdump", path = llvm_bindir + "/llvm-objdump"), + tool_path(name = "strip", path = llvm_bindir + "/llvm-strip"), + ] + + action_configs = [ + action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/clang")]) + for name in [ACTION_NAMES.c_compile] + ] + [ + action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/clang++")]) + for name in all_cpp_compile_actions + ] + [ + action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/clang++")]) + for name in all_link_actions + ] + [ + action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/llvm-ar")]) + for name in [ACTION_NAMES.cpp_link_static_library] + ] + [ + action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/llvm-strip")]) + for name in [ACTION_NAMES.strip] + ] + + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = ([ + flag_group( + flags = [ + "-Wall", + "-Wextra", + "-Wthread-safety", + "-Wself-assign", + # Unfortunately, LLVM isn't clean for this warning. + "-Wno-unused-parameter", + # We use partial sets of designated initializers in + # test code. + "-Wno-missing-field-initializers", + "-fcolor-diagnostics", + ], + ), + ]), + ), + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_header_parsing, + ], + flag_groups = [ + flag_group( + flags = ["-MD", "-MF", "%{dependency_file}"], + expand_if_available = "dependency_file", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group( + flags = ["-frandom-seed=%{output_file}"], + expand_if_available = "output_file", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group(flags = ["-fPIC"], expand_if_available = "pic"), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + flags = ["-gsplit-dwarf"], + expand_if_available = "per_object_debug_info_file", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group( + flags = ["-D%{preprocessor_defines}"], + iterate_over = "preprocessor_defines", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group( + flags = ["-include", "%{includes}"], + iterate_over = "includes", + expand_if_available = "includes", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group( + flags = ["-iquote", "%{quote_include_paths}"], + iterate_over = "quote_include_paths", + ), + flag_group( + flags = ["-I%{include_paths}"], + iterate_over = "include_paths", + ), + flag_group( + flags = ["-isystem", "%{system_include_paths}"], + iterate_over = "system_include_paths", + ), + ], + ), + flag_set( + actions = all_compile_actions, + flag_groups = ([ + flag_group( + flags = ["-g"], + ), + ]), + with_features = [with_feature_set(features = ["dbg"])], + ), + flag_set( + actions = all_compile_actions, + flag_groups = ([ + flag_group( + flags = [ + "-g0", + "-O3", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + # Even when optimizing, preserve frame pointers for profiling. + "-fno-omit-frame-pointer", + "-mno-omit-leaf-frame-pointer", + ], + ), + ]), + with_features = [with_feature_set(features = ["opt"])], + ), + flag_set( + actions = all_cpp_compile_actions, + flag_groups = ([ + flag_group( + flags = [ + "-std=c++17", + "-stdlib=libc++", + ], + ), + ]), + ), + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = ["%{user_compile_flags}"], + iterate_over = "user_compile_flags", + expand_if_available = "user_compile_flags", + ), + ], + ), + flag_set( + actions = all_compile_actions, + flag_groups = ([ + flag_group( + flags = [ + "-no-canonical-prefixes", + "-Wno-builtin-macro-redefined", + "-D__DATE__=\"redacted\"", + "-D__TIMESTAMP__=\"redacted\"", + "-D__TIME__=\"redacted\"", + ], + ), + ]), + ), + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + expand_if_available = "source_file", + flags = ["-c", "%{source_file}"], + ), + flag_group( + expand_if_available = "output_assembly_file", + flags = ["-S"], + ), + flag_group( + expand_if_available = "output_preprocess_file", + flags = ["-E"], + ), + flag_group( + expand_if_available = "output_file", + flags = ["-o", "%{output_file}"], + ), + ], + ), + ], + ) + + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [flag_group(flags = ["-shared"])], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["%{linkstamp_paths}"], + iterate_over = "linkstamp_paths", + expand_if_available = "linkstamp_paths", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-o", "%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.cpp_link_executable, + ], + flag_groups = [ + flag_group( + flags = ["-pie"], + expand_if_available = "force_pic", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = ([ + flag_group( + flags = [ + "-fuse-ld=lld", + "-Wl,-no-as-needed", + # Force the C++ standard library to be statically + # linked. This works even with libc++ despite the + # name, however we have to manually link the ABI + # library and libunwind. + "-static-libstdc++", + # Link with libc++. + "-stdlib=libc++", + # Force static linking with libc++abi as well. + "-l:libc++abi.a", + # Link with Clang's runtime library. This is always + # linked statically. + #"-rtlib=compiler-rt", + ], + ), + ]), + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-L%{library_search_directories}"], + iterate_over = "library_search_directories", + expand_if_available = "library_search_directories", + ), + ], + ), + flag_set( + flag_groups = [ + flag_group( + iterate_over = "runtime_library_search_directories", + flags = [ + "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}", + ], + expand_if_available = + "runtime_library_search_directories", + ), + ], + actions = all_link_actions, + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-Wl,--gdb-index"], + expand_if_available = "is_using_fission", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-Wl,-S"], + expand_if_available = "strip_debug_symbols", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["-Wl,--start-lib"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flags = ["-Wl,-whole-archive"], + expand_if_true = + "libraries_to_link.is_whole_archive", + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "interface_library", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "static_library", + ), + ), + flag_group( + flags = ["-l%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "dynamic_library", + ), + ), + flag_group( + flags = ["-l:%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "versioned_dynamic_library", + ), + ), + flag_group( + flags = ["-Wl,-no-whole-archive"], + expand_if_true = "libraries_to_link.is_whole_archive", + ), + flag_group( + flags = ["-Wl,--end-lib"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["%{user_link_flags}"], + iterate_over = "user_link_flags", + expand_if_available = "user_link_flags", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.cpp_link_static_library, + ] + all_link_actions, + flag_groups = [ + flag_group( + expand_if_available = "linker_param_file", + flags = ["@%{linker_param_file}"], + ), + ], + ), + ], + ) + + sysroot_feature = feature( + name = "sysroot", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions + all_link_actions, + flag_groups = [ + flag_group( + flags = ["--sysroot=%{sysroot}"], + expand_if_available = "sysroot", + ), + ], + ), + ], + ) + + default_archiver_flags_feature = feature( + name = "default_archiver_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group(flags = ["rcsD"]), + flag_group( + flags = ["%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + flag_set( + actions = [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + ], + ), + ], + ) + + use_module_maps = feature( + name = "use_module_maps", + requires = [feature_set(features = ["module_maps"])], + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + # These flag groups are separate so they do not expand to + # the cross product of the variables. + flag_group(flags = ["-fmodule-name=%{module_name}"]), + flag_group( + flags = ["-fmodule-map-file=%{module_map_file}"], + ), + ], + ), + ], + ) + + # Tell bazel we support module maps in general, so they will be generated + # for all c/c++ rules. + # Note: not all C++ rules support module maps; thus, do not imply this + # feature from other features - instead, require it. + module_maps = feature( + name = "module_maps", + enabled = True, + implies = [ + # "module_map_home_cwd", + # "module_map_without_extern_module", + # "generate_submodules", + ], + ) + + layering_check = feature( + name = "layering_check", + implies = ["use_module_maps"], + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group(flags = [ + "-fmodules-strict-decluse", + "-Wprivate-header", + ]), + flag_group( + iterate_over = "dependent_module_map_files", + flags = [ + "-fmodule-map-file=%{dependent_module_map_files}", + ], + ), + ], + ), + ], + ) + fuzzer = feature( + name = "fuzzer", + flag_sets = [ + flag_set( + actions = all_compile_actions + all_link_actions, + flag_groups = [ + flag_group( + flags = ["-fsanitize=fuzzer,address"], + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = ([ + flag_group( + flags = [ + "-static-libsan", + ], + ), + ]), + ), + ], + ) + + features = [ + feature( + name = "no_legacy_features", + ), + feature( + name = "supports_pic", + enabled = True, + ), + feature( + name = "supports_start_end_lib", + enabled = True, + ), + default_compile_flags_feature, + default_archiver_flags_feature, + default_link_flags_feature, + feature(name = "supports_dynamic_linker", enabled = True), + feature(name = "dbg"), + feature(name = "opt"), + sysroot_feature, + fuzzer, + module_maps, + layering_check, + use_module_maps, + ] + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + action_configs = action_configs, + cxx_builtin_include_directories = clang_include_dirs_list + [ + # Append the share directory for sanitizer data files. + clang_resource_dir + "/share", + ], + toolchain_identifier = "local", + host_system_name = "local", + target_system_name = "local", + target_cpu = "k8", + target_libc = "local", + compiler = "local", + abi_version = "local", + abi_libc_version = "local", + tool_paths = tool_paths, + ) + +cc_toolchain_config = rule( + implementation = _impl, + provides = [CcToolchainConfigInfo], +) diff --git a/bazel/cc_toolchains/clang_detected_variables.tpl.bzl b/bazel/cc_toolchains/clang_detected_variables.tpl.bzl new file mode 100644 index 000000000000..848471234ffd --- /dev/null +++ b/bazel/cc_toolchains/clang_detected_variables.tpl.bzl @@ -0,0 +1,13 @@ +# 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 Clang configuration variables. + +This file gets processed by a repository rule, substituting the +`{VARIABLE}`s with values detected by invoking Clang. +""" + +llvm_bindir = "{LLVM_BINDIR}" +clang_resource_dir = "{CLANG_RESOURCE_DIR}" +clang_include_dirs_list = {CLANG_INCLUDE_DIRS_LIST} diff --git a/bazel/cc_toolchains/clang_detection.bzl b/bazel/cc_toolchains/clang_detection.bzl new file mode 100644 index 000000000000..1ef719a81a1a --- /dev/null +++ b/bazel/cc_toolchains/clang_detection.bzl @@ -0,0 +1,169 @@ +# 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 repository rules to detect and configure Clang (and LLVM) toolchain. + +These rules should be run from the `WORKSPACE` file to substitute appropriate +configured values into a `clang_detected_variables.bzl` file that can be used +by the actual toolchain configuration. +""" + +# Tools that we verify are present as part of the detected Clang & LLVM toolchain. +_CLANG_LLVM_TOOLS = [ + "llvm-ar", + "ld.lld", + "clang-cpp", + "clang", + "clang++", + "llvm-dwp", + "llvm-cov", + "llvm-nm", + "llvm-objcopy", + "llvm-strip", +] + +def _run(repository_ctx, cmd): + """Runs the provided `cmd`, checks for failure, and returns the result.""" + exec_result = repository_ctx.execute(cmd, timeout = 10) + if exec_result.return_code != 0: + fail("Unable to run command successfully: %s" % str(cmd)) + + return exec_result + +def _find_clang(repository_ctx): + """Returns the path to a Clang executable if it can find one. + + This assumes the `CC` environment variable points to a Clang binary or + looks for one on the path. + """ + cc_env = repository_ctx.os.environ.get("CC") + if not cc_env: + # Without a specified `CC` name, simply look for `clang`. + clang = repository_ctx.which("clang") + elif "/" not in cc_env: + # Lookup relative `CC` names according to the system `PATH`. + clang = repository_ctx.which(cc_env) + else: + # An absolute `CC` path is simply be used directly. + clang = repository_ctx.path(cc_env) + if not clang.exists: + fail(("The `CC` environment variable is set to a path (`%s`) " + + "that doesn't exist.") % cc_env) + + # Check if either of the `which` invocations fail. + if not clang: + missing = "`clang`" + if cc_env: + missing = "`%s` (from the `CC` environment variable)" % cc_env + fail("Unable to find the %s executable on the PATH." % missing) + + version_output = _run(repository_ctx, [clang, "--version"]).stdout + if "clang" not in version_output: + fail(("Selected Clang executable (`%s`) does not appear to actually " + + "be Clang.") % clang) + + # Make sure this is part of a complete Clang and LLVM toolchain. + for tool in _CLANG_LLVM_TOOLS: + if not clang.dirname.get_child(tool).exists: + fail(("Couldn't find executable `%s` that is expected to be part " + + "of the Clang and LLVM toolchain detected with `%s`.") % + (tool, clang)) + + return clang + +def _compute_clang_resource_dir(repository_ctx, clang): + """Runs the `clang` binary to get its resource dir.""" + output = _run( + repository_ctx, + [clang, "-no-canonical-prefixes", "--print-resource-dir"], + ).stdout + + # The only line printed is this path. + return output.splitlines()[0] + +def _compute_clang_cpp_include_search_paths(repository_ctx, clang): + """Runs the `clang` binary and extracts the include search paths. + + Returns the resulting paths as a list of strings. + """ + + # The only way to get this out of Clang currently is to parse the verbose + # output of the compiler when it is compiling C++ code. + cmd = [ + clang, + # Avoid canonicalizing away symlinks. + "-no-canonical-prefixes", + # Extract verbose output. + "-v", + # Just parse the input, don't generate outputs. + "-fsyntax-only", + # Use libc++ rather than any other standard library. + "-stdlib=libc++", + # Force the language to be C++. + "-x", + "c++", + # Read in an empty input file. + "/dev/null", + ] + + # Note that verbose output is on stderr, not stdout! + output = _run(repository_ctx, cmd).stderr.splitlines() + + # Return the list of directories printed for system headers. These are the + # only ones that Bazel needs us to manually provide. We find these by + # searching for a begin and end marker. We also have to strip off a leading + # space from each path. + include_begin = output.index("#include <...> search starts here:") + 1 + include_end = output.index("End of search list.", include_begin) + return [ + repository_ctx.path(s.lstrip(" ")) + for s in output[include_begin:include_end] + ] + +def _detect_clang_toolchain_impl(repository_ctx): + # First just symlink in the untemplated parts of the toolchain repo. + repository_ctx.symlink(repository_ctx.attr._clang_toolchain_build, "BUILD") + repository_ctx.symlink( + repository_ctx.attr._clang_cc_toolchain_config, + "cc_toolchain_config.bzl", + ) + + clang = _find_clang(repository_ctx) + resource_dir = _compute_clang_resource_dir(repository_ctx, clang) + include_dirs = _compute_clang_cpp_include_search_paths( + repository_ctx, + clang, + ) + + repository_ctx.template( + "clang_detected_variables.bzl", + repository_ctx.attr._clang_detected_variables_template, + substitutions = { + "{LLVM_BINDIR}": str(clang.dirname), + "{CLANG_RESOURCE_DIR}": resource_dir, + "{CLANG_INCLUDE_DIRS_LIST}": str([str(path) for path in include_dirs]), + }, + executable = False, + ) + +detect_clang_toolchain = repository_rule( + implementation = _detect_clang_toolchain_impl, + configure = True, + local = True, + attrs = { + "_clang_toolchain_build": attr.label( + default = Label("//bazel/cc_toolchains:clang_toolchain.BUILD"), + allow_single_file = True, + ), + "_clang_cc_toolchain_config": attr.label( + default = Label("//bazel/cc_toolchains:clang_cc_toolchain_config.bzl"), + allow_single_file = True, + ), + "_clang_detected_variables_template": attr.label( + default = Label("//bazel/cc_toolchains:clang_detected_variables.tpl.bzl"), + allow_single_file = True, + ), + }, + environ = ["CC"], +) diff --git a/bazel/cc_toolchains/clang_toolchain.BUILD b/bazel/cc_toolchains/clang_toolchain.BUILD new file mode 100644 index 000000000000..9192c816007d --- /dev/null +++ b/bazel/cc_toolchains/clang_toolchain.BUILD @@ -0,0 +1,44 @@ +# 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 + +# This file is symlinked into a configured Clang toolchain repository as the +# root `BUILD` file for that repository. + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_toolchain", "cc_toolchain_suite") +load(":cc_toolchain_config.bzl", "cc_toolchain_config") + +cc_library( + name = "malloc", +) + +filegroup( + name = "empty", + srcs = [], +) + +cc_toolchain_suite( + name = "bazel_cc_toolchain", + toolchains = { + "k8": ":cc-compiler-k8", + }, +) + +cc_toolchain( + name = "cc-compiler-k8", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 1, + toolchain_config = ":local", + toolchain_identifier = "local", +) + +cc_toolchain_config( + name = "local", +) diff --git a/bazel/fuzzing/BUILD b/bazel/fuzzing/BUILD new file mode 100644 index 000000000000..74906e61dd59 --- /dev/null +++ b/bazel/fuzzing/BUILD @@ -0,0 +1,5 @@ +# 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 + +# Empty BUILD file to allow access to Bazel extensions. diff --git a/bazel/fuzzing/rules.bzl b/bazel/fuzzing/rules.bzl new file mode 100644 index 000000000000..40a737f1da64 --- /dev/null +++ b/bazel/fuzzing/rules.bzl @@ -0,0 +1,51 @@ +# 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 + +"""Rules for building fuzz tests.""" + +load("@rules_cc//cc:defs.bzl", "cc_test") + +def cc_fuzz_test( + name, + corpus = None, + args = [], + data = [], + features = [], + tags = [], + **kwargs): + """Macro for C++ fuzzing test. + + Args: + name: The main fuzz test rule name. + corpus: List of files to use as a fuzzing corpus. + args: Will have the locations of the corpus files added and passed down + to the fuzz test. + data: Will have the corpus added and passed down to the fuzz test. + features: Will have the "fuzzer" feature added and passed down to the + fuzz test. + tags: Will have "fuzz_test" added and passed down to the fuzz test. + **kwargs: Remaining arguments passed down to the fuzz test. + """ + + # Add relevant tag and feature if necessary. + if "fuzz_test" not in tags: + tags = tags + ["fuzz_test"] + if "fuzzer" not in features: + features = features + ["fuzzer"] + + # Append the corpus files to the test arguments. When run on a list of + # files rather than a directory, libFuzzer-based fuzzers will perform a + # regression test against the corpus. + if corpus: + data = data + corpus + args = args + ["$(location %s)" % file for file in corpus] + + cc_test( + name = name, + args = args, + data = data, + features = features, + tags = tags, + **kwargs + ) diff --git a/third_party/llvm-bazel b/third_party/llvm-bazel new file mode 160000 index 000000000000..1536b141d636 --- /dev/null +++ b/third_party/llvm-bazel @@ -0,0 +1 @@ +Subproject commit 1536b141d636e95f2cc3c02c5891b23e322fb0c8 diff --git a/third_party/update_llvm.sh b/third_party/update_llvm.sh new file mode 100755 index 000000000000..88e5d0947f31 --- /dev/null +++ b/third_party/update_llvm.sh @@ -0,0 +1,19 @@ +#!/bin/bash -eux +# 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 + +# Ensure the working directory is the script's directory. +cd "$(dirname "$0")" + +# Update the `llvm-bazel` project first by just pulling from its HEAD. +git submodule update --remote llvm-bazel + +# Find the current LLVM commit in the `llvm-bazel` project. +llvm_commit=$(cd llvm-bazel; git submodule status third_party/llvm-project | awk '/-[0-9a-f]+ / { print substr($1, 2) }') + +# Fetch and checkout this commit of LLVM +cd llvm-project +git fetch +git checkout $llvm_commit +cd ..