From 994e75ad0dfe6190539402b9296b9cdc8c543a8d Mon Sep 17 00:00:00 2001 From: Oliver Lee Date: Fri, 21 Jul 2023 03:26:05 -0700 Subject: [PATCH] Setup bazel build and check workflow in CI (#1) Set up Bazel with hermetic GCC 12 and Clang 16 toolchains, ClangFormat, ClangTidy, sanitizers, and Buildifier. boost.ut is introduced as the test framework library. This commit also sets up a check workflow in CI testing the above mentioned tools. This is largely copied the configuration from https://github.com/garymm/gpu-deflate/ Change-Id: I71bfc757c90a13277ac38baca15539e114b6b5d3 --- .bazeliskrc | 1 + .bazelrc | 25 ++++ .clang-format | 77 ++++++++++ .clang-tidy | 65 +++++++++ .github/workflows/check.yml | 95 ++++++++++++ .github/workflows/ci.bazelrc | 11 ++ .gitignore | 4 + BUILD.bazel | 42 ++++++ README.md | 15 ++ WORKSPACE.bazel | 178 ++++++++++++++++++++++ test/BUILD.bazel | 10 ++ test/dummy_test.cpp | 9 ++ toolchain/.clang-tidy | 9 ++ toolchain/BUILD.bazel | 106 ++++++++++++++ toolchain/asan_feature_available.cpp | 18 +++ toolchain/runtime_failure.sh | 12 ++ toolchain/std_expected_available.cpp | 6 + toolchain/tsan_feature_available.cpp | 24 +++ toolchain/ubsan_feature_available.cpp | 12 ++ tools/BUILD.bazel | 19 +++ tools/host_system_libraries.bzl | 38 +++++ tools/llvm_toolchain.bzl | 203 ++++++++++++++++++++++++++ tools/llvm_toolchain_dependencies.bzl | 12 ++ tools/local_config_info.bzl | 20 +++ 24 files changed, 1011 insertions(+) create mode 100644 .bazeliskrc create mode 100644 .bazelrc create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .github/workflows/check.yml create mode 100644 .github/workflows/ci.bazelrc create mode 100644 .gitignore create mode 100644 BUILD.bazel create mode 100644 README.md create mode 100644 WORKSPACE.bazel create mode 100644 test/BUILD.bazel create mode 100644 test/dummy_test.cpp create mode 100644 toolchain/.clang-tidy create mode 100644 toolchain/BUILD.bazel create mode 100644 toolchain/asan_feature_available.cpp create mode 100755 toolchain/runtime_failure.sh create mode 100644 toolchain/std_expected_available.cpp create mode 100644 toolchain/tsan_feature_available.cpp create mode 100644 toolchain/ubsan_feature_available.cpp create mode 100644 tools/BUILD.bazel create mode 100644 tools/host_system_libraries.bzl create mode 100644 tools/llvm_toolchain.bzl create mode 100644 tools/llvm_toolchain_dependencies.bzl create mode 100644 tools/local_config_info.bzl diff --git a/.bazeliskrc b/.bazeliskrc new file mode 100644 index 0000000..35cd375 --- /dev/null +++ b/.bazeliskrc @@ -0,0 +1 @@ +USE_BAZEL_VERSION=6.2.1 diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..efa56af --- /dev/null +++ b/.bazelrc @@ -0,0 +1,25 @@ +build --incompatible_enable_cc_toolchain_resolution +build --action_env="BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1" + +build:clang16 --extra_toolchains=//toolchain:clang16 +build:gcc12 --extra_toolchains=//toolchain:gcc12 + +build:clang-format --aspects @bazel_clang_format//:defs.bzl%clang_format_aspect +build:clang-format --@bazel_clang_format//:binary=@llvm_16_toolchain//:clang-format +build:clang-format --@bazel_clang_format//:config=//:format_config +build:clang-format --output_groups=report +build:clang-format --keep_going + +build:clang-tidy-base --config=clang16 +build:clang-tidy-base --aspects @bazel_clang_tidy//clang_tidy:clang_tidy.bzl%clang_tidy_aspect +build:clang-tidy-base --@bazel_clang_tidy//:clang_tidy_config=//:tidy_config +build:clang-tidy-base --output_groups=report +build:clang-tidy-base --keep_going + +build:verbose-clang-tidy --config=clang-tidy-base +build:verbose-clang-tidy --@bazel_clang_tidy//:clang_tidy_executable=//tools:verbose-clang-tidy + +build:clang-tidy --config=clang-tidy-base +build:clang-tidy --@bazel_clang_tidy//:clang_tidy_executable=@llvm_16_toolchain//:clang-tidy + +try-import %workspace%/user.bazelrc diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8bc455a --- /dev/null +++ b/.clang-format @@ -0,0 +1,77 @@ +--- +BasedOnStyle: LLVM +AlignAfterOpenBracket: Align +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BreakAfterAttributes: Always +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterEnum: true + AfterFunction: true + AfterStruct: true + AfterUnion: true + SplitEmptyFunction: false + SplitEmptyRecord: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +Cpp11BracedListStyle: true +DerivePointerAlignment: false +FixNamespaceComments: true +IncludeBlocks: Regroup +IndentCaseLabels: true +IndentWidth: 2 +InsertNewlineAtEOF: true +Language: Cpp +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PenaltyIndentedWhitespace: 200 +PenaltyReturnTypeOnItsOwnLine: 1 +PointerAlignment: Left +QualifierAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterRequiresInClause: true + AfterRequiresInExpression: true +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest + +IncludeCategories: + - Regex: '"test' + Priority: 2 + - Regex: '"fmt' + Priority: 3 + - Regex: 'metal.hpp' + Priority: 3 + - Regex: 'boost/ut.hpp' + Priority: 4 + - Regex: '<[[:alnum:|_].]+>' + Priority: 5 + - Regex: '".*"' + Priority: 1 + +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..beb0a91 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,65 @@ +UseColor: true + +Checks: > + bugprone-*, + clang-analyzer-cplusplus*, + concurrency-*, + cppcoreguidelines-*, + misc-*, + modernize-*, + performance-*, + readability-*, + + # Bazel does this for determinism, + -clang-diagnostic-builtin-macro-redefined, + + # suppress due to assert, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + + # short names are fine for short lifetimes, + -readability-identifier-length, + + # allow unused variables to be unnamed, + -readability-named-parameter, + + # C-arrays necessary as function args, + -modernize-avoid-c-arrays, + + # use iterators as abstractions, not pointers, + -readability-qualified-auto, + + # it's okay for exceptions to escape main, + -bugprone-exception-escape, + + # false positive with spaceship operator, + # https://reviews.llvm.org/D95714?id=320393, + -modernize-use-nullptr, + + # disable common aliases, + -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-c-copy-assignment-signature, + -cppcoreguidelines-explicit-virtual-functions, + -cppcoreguidelines-non-private-member-variables-in-classes, + + # disable EXTREMELY SLOW checks, + -bugprone-reserved-identifier, + -readability-identifier-naming, + -misc-confusable-identifiers, + +CheckOptions: + - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: true + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: false + +# only lint files coming from this project +HeaderFilterRegex: '__main__/' + +# clang-diagnostic-builtin-macro-redefined must be manually suppressed here +# https://github.com/erenon/bazel_clang_tidy/issues/29 +# https://github.com/llvm/llvm-project/issues/56709 +# +WarningsAsErrors: '*,-clang-diagnostic-builtin-macro-redefined' + + diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..0c47784 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,95 @@ +name: check + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "*" ] + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: [gcc12, clang16] + feature: ['', asan, tsan, ubsan] + + steps: + - uses: actions/checkout@v3 + + - name: install libtinfo5 + # clang tools load libtinfo5 for color diagnostics but `ubuntu-latest` + # runners already have `libtinfo.so.6` installed. We just create a + # symlink since it's faster than installing libtinfo5. + # https://github.com/circleci/circleci-images/issues/430#issuecomment-522602495 + run: | + sudo ln -s /lib/x86_64-linux-gnu/libtinfo.so.6 /lib/x86_64-linux-gnu/libtinfo.so.5 + + - run: | + bazel \ + --bazelrc=.github/workflows/ci.bazelrc \ + test \ + --config=${{ matrix.toolchain }} \ + --features=${{ matrix.feature }} \ + //... + + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + flag: + - '--config=clang-format' + - '--config=clang-tidy' + - '--config=verbose-clang-tidy' + - '--compilation_mode=opt' + exclude: + - flag: ${{ github.event_name == 'pull_request' && '--config=verbose-clang-tidy' || 'dummy' }} + + steps: + - uses: actions/checkout@v3 + + - name: install libtinfo5 + # clang tools load libtinfo5 for color diagnostics but `ubuntu-latest` + # runners already have `libtinfo.so.6` installed. We just create a + # symlink since it's faster than installing libtinfo5. + # https://github.com/circleci/circleci-images/issues/430#issuecomment-522602495 + run: | + sudo ln -s /lib/x86_64-linux-gnu/libtinfo.so.6 /lib/x86_64-linux-gnu/libtinfo.so.5 + + - run: | + bazel \ + --bazelrc=.github/workflows/ci.bazelrc \ + build \ + ${{ matrix.flag }} \ + //... + + build-without-terminfo: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: [gcc12, clang16] + + steps: + - uses: actions/checkout@v3 + + - run: | + bazel \ + --bazelrc=.github/workflows/ci.bazelrc \ + build \ + --config=${{ matrix.toolchain }} \ + //... + + buildifier: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - run: | + bazel \ + --bazelrc=.github/workflows/ci.bazelrc \ + run \ + //:buildifier.check diff --git a/.github/workflows/ci.bazelrc b/.github/workflows/ci.bazelrc new file mode 100644 index 0000000..5af51c3 --- /dev/null +++ b/.github/workflows/ci.bazelrc @@ -0,0 +1,11 @@ +# This is from Bazel's former travis setup, to avoid blowing up the RAM usage. +startup --host_jvm_args=-Xmx2500m + +build --show_timestamps +build --announce_rc +build --color=yes +build --terminal_columns=120 +build --remote_download_minimal + +test --test_output=all +test --test_verbose_timeout_warnings diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..098e97d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bazel-* +.envrc + +user.bazelrc diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..a3b6589 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,42 @@ +load("@bazel_clang_format//:defs.bzl", "clang_format_update") +load("@bazel_clang_tidy//:defs.bzl", "clang_tidy_apply_fixes") +load("@buildifier_prebuilt//:rules.bzl", "buildifier") + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "format_config", + srcs = [".clang-format"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "tidy_config", + srcs = [".clang-tidy"], + visibility = ["//visibility:public"], +) + +clang_format_update( + name = "clang-format", + binary = "@llvm_16_toolchain//:clang-format", + config = ":format_config", +) + +clang_tidy_apply_fixes( + name = "clang-tidy-fix", + apply_replacements_binary = "@llvm_16_toolchain//:clang-apply-replacements", + tidy_binary = "@llvm_16_toolchain//:clang-tidy", + tidy_config = ":tidy_config", +) + +buildifier( + name = "buildifier.check", + lint_mode = "warn", + mode = "check", +) + +buildifier( + name = "buildifier.fix", + lint_mode = "warn", + mode = "fix", +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..eeeddec --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# bike2 + +A successor to [bike1](https://github.com/oliverlee/robot.bicycle). + +## Set up + +Should work on Linux. + +* Install `bazel` or `bazelisk` + +* Verify that you can build and test: +```sh +bazel test //... +``` + diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel new file mode 100644 index 0000000..9ec4325 --- /dev/null +++ b/WORKSPACE.bazel @@ -0,0 +1,178 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +COMMON_CXX_WARNINGS = [ + "-Werror", + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wconversion", + "-Wnon-virtual-dtor", + "-Wold-style-cast", + "-Wcast-align", + "-Wunused", + "-Woverloaded-virtual", + "-Wmisleading-indentation", + "-Wnull-dereference", + "-Wdouble-promotion", + "-Wformat=2", + "-Wimplicit-fallthrough", +] + +# Bazel sends stdout/stderr to file so we cannot use `-fdiagnostics-color=auto` + +# https://github.com/bazelbuild/bazel/issues/6046#issuecomment-626941725 +# +# Clang will also require libtinfo.so.5 with `-fdiagnostics-color=always` +# https://github.com/mun-lang/mun/issues/191#issuecomment-753402659 +# https://reviews.llvm.org/D42274 +load("//tools:host_system_libraries.bzl", "host_system_libraries") + +host_system_libraries( + name = "host_system_libraries", + find = ["libtinfo.so.5"], +) + +load("@host_system_libraries//:defs.bzl", "HOST_SYSTEM_LIBRARIES") + +# Note: not correct on MacOS. +LLVM_COLOR_FLAGS = ["-fdiagnostics-color=always" if "libinfo.so.5" in HOST_SYSTEM_LIBRARIES else ""] + +BAZEL_BOOTLIN_COMMIT = "eae108c14445074ab5c8ae7f5338eebf1873ae69" + +http_archive( + name = "bazel_bootlin", + sha256 = "ea76bcd8fc27da13bc79acc4844baadc563e07888a128265ca74d07cd58c1f34", + strip_prefix = "bazel_bootlin-%s" % BAZEL_BOOTLIN_COMMIT, + url = "https://github.com/oliverlee/bazel_bootlin/archive/%s.tar.gz" % BAZEL_BOOTLIN_COMMIT, +) + +load("@bazel_bootlin//:defs.bzl", "bootlin_toolchain") + +# for mapping from buildroot version to gcc version +# see https://toolchains.bootlin.com/releases_x86-64.html +bootlin_toolchain( + name = "gcc_12_toolchain", + architecture = "x86-64", + buildroot_version = "bleeding-edge-2022.08-1", + extra_cxx_flags = [ + "-fdiagnostics-color=always", + "-std=c++23", + "-Wduplicated-cond", + "-Wduplicated-branches", + "-Wlogical-op", + "-Wuseless-cast", + "-Wshadow=compatible-local", # Equivalent to -Wshadow on llvm. GCC default is stupid. + ] + COMMON_CXX_WARNINGS, + libc_impl = "glibc", +) + +BAZEL_TOOLCHAIN_VERSION = "c65ef7a45907016a754e5bf5bfabac76eb702fd3" + +http_archive( + name = "bazel_toolchain", + sha256 = "c70777b0aa877085323aa1ced3b6e011811a9800092e837043aa5f678d838301", + strip_prefix = "bazel-toolchain-%s" % BAZEL_TOOLCHAIN_VERSION, + url = "https://github.com/grailbio/bazel-toolchain/archive/%s.tar.gz" % BAZEL_TOOLCHAIN_VERSION, +) + +load("//tools:llvm_toolchain_dependencies.bzl", "llvm_toolchain_dependencies") + +llvm_toolchain_dependencies() + +load("//tools:llvm_toolchain.bzl", "llvm_toolchain") + +llvm_toolchain( + name = "llvm_16_toolchain", + cxx_flags = { + "": [ + "-stdlib=libc++", + "-std=c++2b", + "-Wshadow", + ] + COMMON_CXX_WARNINGS + LLVM_COLOR_FLAGS, + }, + # Link UBSan C++ runtime libraries if the `ubsan` feature is enabled + # https://github.com/bazelbuild/bazel/issues/12797#issuecomment-980641064 + # use `link_libs` to prevent overriding default `link_flags` + # https://github.com/grailbio/bazel-toolchain/blob/ceeedcc4464322e05fe5b8df3749cc02273ee083/toolchain/cc_toolchain_config.bzl#L148-L150 + link_libs = { + "": ["-fsanitize-link-c++-runtime"], + }, + linux_x86_64_sysroot = "@gcc_12_toolchain_files//x86_64-buildroot-linux-gnu/sysroot", + llvm_version = "16.0.4", +) + +# register llvm first, it has better error messages +load("@llvm_16_toolchain//:toolchains.bzl", "llvm_register_toolchains") + +llvm_register_toolchains() + +register_toolchains( + "@gcc_12_toolchain//:toolchain", +) + +BOOST_UT_VERSION = "e53a47d37bc594e80bd5f1b8dc1ade8dce4429d3" + +http_archive( + name = "boost_ut", + build_file_content = """ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "boost_ut", + hdrs = ["include/boost/ut.hpp"], + includes = ["include"], + visibility = ["//visibility:public"], +) +""", + sha256 = "abc90fa389a47504d06a88a56ddc9e580cf2654173bd14d211db714bb111b923", + strip_prefix = "ut-%s" % BOOST_UT_VERSION, + url = "https://github.com/boost-ext/ut/archive/%s.tar.gz" % BOOST_UT_VERSION, +) + +BAZEL_CLANG_FORMAT_VERSION = "f4198b68887699a4d1862e44458e4969ad69fc8a" + +http_archive( + name = "bazel_clang_format", + sha256 = "bd75df6dae8d290a716e1812c463ef4ab36869b5557d8a6dd6abb8315acfc6ac", + strip_prefix = "bazel_clang_format-%s" % BAZEL_CLANG_FORMAT_VERSION, + url = "https://github.com/oliverlee/bazel_clang_format/archive/%s.tar.gz" % BAZEL_CLANG_FORMAT_VERSION, +) + +BAZEL_CLANG_TIDY_VERSION = "aae87699cca19d8f6e84538576ab47587043d1d2" + +http_archive( + name = "bazel_clang_tidy", + sha256 = "ee7d89375b5c6554b40ea1b1132a8cf7e3e269f7c2f6b2f595e4c7181d44b736", + strip_prefix = "bazel_clang_tidy-%s" % BAZEL_CLANG_TIDY_VERSION, + url = "https://github.com/oliverlee/bazel_clang_tidy/archive/%s.tar.gz" % BAZEL_CLANG_TIDY_VERSION, +) + +http_archive( + name = "buildifier_prebuilt", + sha256 = "e46c16180bc49487bfd0f1ffa7345364718c57334fa0b5b67cb5f27eba10f309", + strip_prefix = "buildifier-prebuilt-6.1.0", + urls = [ + "https://github.com/keith/buildifier-prebuilt/archive/6.1.0.tar.gz", + ], +) + +load("@buildifier_prebuilt//:deps.bzl", "buildifier_prebuilt_deps") + +buildifier_prebuilt_deps() + +load("@buildifier_prebuilt//:defs.bzl", "buildifier_prebuilt_register_toolchains") + +buildifier_prebuilt_register_toolchains() + +COMPILE_COMMANDS_VERSION = "3dddf205a1f5cde20faf2444c1757abe0564ff4c" + +http_archive( + name = "hedron_compile_commands", + sha256 = "3cd0e49f0f4a6d406c1d74b53b7616f5e24f5fd319eafc1bf8eee6e14124d115", + strip_prefix = "bazel-compile-commands-extractor-%s" % COMPILE_COMMANDS_VERSION, + url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/%s.tar.gz" % COMPILE_COMMANDS_VERSION, +) + +load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup") + +hedron_compile_commands_setup() diff --git a/test/BUILD.bazel b/test/BUILD.bazel new file mode 100644 index 0000000..9953459 --- /dev/null +++ b/test/BUILD.bazel @@ -0,0 +1,10 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "dummy_test", + timeout = "short", + srcs = ["dummy_test.cpp"], + deps = [ + "@boost_ut", + ], +) diff --git a/test/dummy_test.cpp b/test/dummy_test.cpp new file mode 100644 index 0000000..c17795c --- /dev/null +++ b/test/dummy_test.cpp @@ -0,0 +1,9 @@ +#include + +auto main() -> int +{ + using ::boost::ut::expect; + using ::boost::ut::test; + + test("true is true") = [] { expect(true); }; +} diff --git a/toolchain/.clang-tidy b/toolchain/.clang-tidy new file mode 100644 index 0000000..036c0d8 --- /dev/null +++ b/toolchain/.clang-tidy @@ -0,0 +1,9 @@ +Checks: > + -*, + + # dummy, see below, + misc-definitions-in-headers, + +CheckOptions: + - key: HeaderFileExtensions + value: "x" diff --git a/toolchain/BUILD.bazel b/toolchain/BUILD.bazel new file mode 100644 index 0000000..1d19cf6 --- /dev/null +++ b/toolchain/BUILD.bazel @@ -0,0 +1,106 @@ +load("@rules_cc//cc:defs.bzl", "cc_binary") + +package(features = [ + "-asan", + "-tsan", + "-ubsan", +]) + +# Verify that the selected C++ toolchain provides std::expected +cc_binary( + name = "std_expected_available", + srcs = ["std_expected_available.cpp"], +) + +alias( + name = "gcc12", + actual = "@gcc_12_toolchain//:toolchain", +) + +alias( + name = "gcc", + actual = ":gcc12", +) + +alias( + name = "clang16", + actual = select({ + "@platforms//os:macos": "@llvm_16_toolchain//:cc-toolchain-aarch64-darwin", + "//conditions:default": "@llvm_16_toolchain//:cc-toolchain-x86_64-linux", + }), +) + +alias( + name = "clang", + actual = ":clang16", +) + +# Doesn't work on MacOS: https://github.com/grailbio/bazel-toolchain/issues/192 +config_setting( + name = "linux", + constraint_values = ["@platforms//os:linux"], +) + +linux_only = select({ + "linux": [], + "//conditions:default": ["@platforms//:incompatible"], +}) + +cc_binary( + name = "asan_feature", + srcs = ["asan_feature_available.cpp"], + copts = ["-O0"], + features = ["asan"], + target_compatible_with = linux_only, +) + +sh_test( + name = "asan_feature_available", + timeout = "short", + srcs = ["runtime_failure.sh"], + args = [ + "$(location :asan_feature)", + "ERROR: AddressSanitizer: stack-use-after-scope", + ], + data = [":asan_feature"], +) + +cc_binary( + name = "tsan_feature", + srcs = ["tsan_feature_available.cpp"], + features = ["tsan"], + target_compatible_with = linux_only, +) + +sh_test( + name = "tsan_feature_available", + timeout = "short", + srcs = ["runtime_failure.sh"], + args = [ + "$(location :tsan_feature)", + "WARNING: ThreadSanitizer: data race", + ], + data = [":tsan_feature"], +) + +cc_binary( + name = "ubsan_feature", + srcs = ["ubsan_feature_available.cpp"], + copts = [ + "-O0", + "-Wno-array-bounds", + ], + features = ["ubsan"], + target_compatible_with = linux_only, +) + +sh_test( + name = "ubsan_feature_available", + timeout = "short", + srcs = ["runtime_failure.sh"], + args = [ + "$(location :ubsan_feature)", + "runtime error: index 10 out of bounds", + ], + data = [":ubsan_feature"], +) diff --git a/toolchain/asan_feature_available.cpp b/toolchain/asan_feature_available.cpp new file mode 100644 index 0000000..ce02934 --- /dev/null +++ b/toolchain/asan_feature_available.cpp @@ -0,0 +1,18 @@ +// https://github.com/bazelbuild/bazel/blob/abae5ca3e8142f93cf0c2597e3410ed955c4dd59/src/test/shell/bazel/cc_integration_test.sh#L1888-L1897 + +// clang-format off +// NOLINTBEGIN + +int main() { + volatile int* p; + + { + volatile int x = 0; + p = &x; + } + + return *p; +} + +// NOLINTEND +// clang-format on diff --git a/toolchain/runtime_failure.sh b/toolchain/runtime_failure.sh new file mode 100755 index 0000000..ca3aaaf --- /dev/null +++ b/toolchain/runtime_failure.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -euo pipefail + +bin=$1 +err=$2 + +ret=0 +$bin &> log || ret=$? +[ $ret -ne 0 ] + +grep -q $err log diff --git a/toolchain/std_expected_available.cpp b/toolchain/std_expected_available.cpp new file mode 100644 index 0000000..a0f5824 --- /dev/null +++ b/toolchain/std_expected_available.cpp @@ -0,0 +1,6 @@ +#include + +auto main() -> int +{ + [[maybe_unused]] const auto x = std::expected{}; +} diff --git a/toolchain/tsan_feature_available.cpp b/toolchain/tsan_feature_available.cpp new file mode 100644 index 0000000..3fd1060 --- /dev/null +++ b/toolchain/tsan_feature_available.cpp @@ -0,0 +1,24 @@ +// https://github.com/bazelbuild/bazel/blob/abae5ca3e8142f93cf0c2597e3410ed955c4dd59/src/test/shell/bazel/cc_integration_test.sh#L1918-L1933 + +// clang-format off +// NOLINTBEGIN + +#include + +int value = 0; + +void increment() { + ++value; +} + +int main() { + std::thread t1(increment); + std::thread t2(increment); + t1.join(); + t2.join(); + + return value; +} + +// NOLINTEND +// clang-format on diff --git a/toolchain/ubsan_feature_available.cpp b/toolchain/ubsan_feature_available.cpp new file mode 100644 index 0000000..06fc1c2 --- /dev/null +++ b/toolchain/ubsan_feature_available.cpp @@ -0,0 +1,12 @@ +// https://github.com/bazelbuild/bazel/blob/abae5ca3e8142f93cf0c2597e3410ed955c4dd59/src/test/shell/bazel/cc_integration_test.sh#LL1954C1-L1957C2 + +// clang-format off +// NOLINTBEGIN + +int main() { + int array[10]; + return array[10]; +} + +// NOLINTEND +// clang-format on diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel new file mode 100644 index 0000000..d57a3a4 --- /dev/null +++ b/tools/BUILD.bazel @@ -0,0 +1,19 @@ +genrule( + name = "gen_verbose_clang_tidy", + srcs = [], + outs = ["verbose-clang-tidy.sh"], + # echo "$(dirname bazel-out/k8-fastbuild/bin/tools/verbose-clang-tidy.sh)/../) ..." + cmd = """ +echo "$$(dirname $@)/../$(rootpath {tidy}) --enable-check-profile \\$$@" > $@ +""".format( + tidy = "@llvm_16_toolchain//:clang-tidy", + ), + executable = True, + tools = ["@llvm_16_toolchain//:clang-tidy"], +) + +sh_binary( + name = "verbose-clang-tidy", + srcs = ["verbose-clang-tidy.sh"], + data = ["@llvm_16_toolchain//:clang-tidy"], +) diff --git a/tools/host_system_libraries.bzl b/tools/host_system_libraries.bzl new file mode 100644 index 0000000..075e87e --- /dev/null +++ b/tools/host_system_libraries.bzl @@ -0,0 +1,38 @@ +""" +Repository rule used to determine if a library is installed in the host system. +""" + +def _host_system_libraries_impl(rctx): + rctx.file("BUILD.bazel", executable = False) + + system_libs = rctx.execute(["ldconfig", "-p"]).stdout + + found = [ + '"' + lib + '"' + for lib in rctx.attr.find + if lib in system_libs + ] + + rctx.file( + "defs.bzl", + executable = False, + content = "HOST_SYSTEM_LIBRARIES = [{}]".format(", ".join(found)), + ) + +host_system_libraries = repository_rule( + implementation = _host_system_libraries_impl, + attrs = { + "find": attr.string_list( + mandatory = True, + doc = """ + List of libraries to detect. Each library is added to `HOST_SYSTEM_LIBRARIES` + if found. + """, + ), + }, + doc = """ + "A repository rule for checking if system libraries are installed on the + host platform. Creates a workspace with a `defs.bzl` file containing a + `HOST_SYSTEM_LIBRARIES` list. + """, +) diff --git a/tools/llvm_toolchain.bzl b/tools/llvm_toolchain.bzl new file mode 100644 index 0000000..3e13820 --- /dev/null +++ b/tools/llvm_toolchain.bzl @@ -0,0 +1,203 @@ +""" +Provides a wrapper around `llvm_toolchain` that uses a package provided sysroot +instead of the system installed libraries. +""" + +load( + "@bazel_toolchain//toolchain:deps.bzl", + _llvm_toolchain_dependencies = "bazel_toolchain_dependencies", +) +load( + "@bazel_toolchain//toolchain:rules.bzl", + _llvm_toolchain = "llvm_toolchain", + _llvm_toolchain_files = "llvm", +) +load("@local_config_info//:defs.bzl", "BAZEL_EXTERNAL_DIR") + +# Rename to (hopefully) be less confusing +llvm_toolchain_dependencies = _llvm_toolchain_dependencies + +def _patch_dynamic_linker_impl(rctx): + label = Label(rctx.attr.base_workspace + ":BUILD.bazel") + base_workspace = str(rctx.path(label).realpath).removesuffix("/BUILD.bazel") + this_workspace = str(rctx.path(".")) + + files = rctx.execute( + ["ls", base_workspace], + ).stdout.replace("WORKSPACE", "").replace("bin", "").strip().split("\n") + + for f in files: + source = base_workspace + "/" + f + dest = this_workspace + "/" + f + rctx.execute(["ln", "-s", source, dest]) + + rctx.execute(["cp", "-r", base_workspace + "/bin", "real-bin"]) + + for tool in rctx.execute(["ls", "real-bin"]).stdout.strip().split("\n"): + rctx.file( + "bin/" + tool, + content = """#!/bin/bash + exec "{dynamic_linker}" --library-path "{paths}" "{real_tool}" "$@" + """.format( + dynamic_linker = rctx.attr.dynamic_linker, + paths = ":".join(rctx.attr.library_paths), + real_tool = this_workspace + "/real-bin/" + tool, + ), + ) + +_patch_dynamic_linker = repository_rule( + implementation = _patch_dynamic_linker_impl, + attrs = { + "dynamic_linker": attr.string( + mandatory = True, + doc = """ + Absolute path to an alternate dynamic linker. + """, + ), + "library_paths": attr.string_list( + mandatory = True, + doc = """ + Absolute paths for the library search directories. These are used + along with the dynamic linker. + """, + ), + "base_workspace": attr.string( + mandatory = True, + doc = """ + Base workspace containing binaries to patch. + """, + ), + }, + doc = """ + Copies the contents of `base_workspace` but replaces all files in the `bin` + directory with wrapper scripts. These wrapper scripts use the specified + `dynamic_linker`, allowing runtime use of libraries in `library_paths`. + """, +) + +def _extend_linux_x86_64_key(arg_dict, values): + existing = arg_dict.get("linux-x86_64", arg_dict[""]) + arg_dict["linux-x86_64"] = existing + values + return arg_dict + +def llvm_toolchain(linux_x86_64_sysroot = "", **kwargs): + # buildifier: disable=function-docstring-args + """ + Wraps `llvm_toolchain`, forcing use of a user-provided sysroot on linux-x86_64. + + Args: + linux_x86_64_sysroot: string + label of a sysroot + + for other args, see + https://github.com/grailbio/bazel-toolchain/blob/master/toolchain/rules.bzl + """ + + if not linux_x86_64_sysroot: + fail(""" + This rule requires the sysroot to be specified for linux-x86_64 + platforms. If this not desired, use `llvm_toolchain` from + `@bazel_toolchain` directly. + """) + + linux_x86_64_sysroot_pkg = linux_x86_64_sysroot + linux_x86_64_sysroot_path = ( + BAZEL_EXTERNAL_DIR + + linux_x86_64_sysroot_pkg.replace("@", "/").replace("//", "/") + ) + + if "sysroot" in kwargs or "toolchain_roots" in kwargs: + fail(""" + `sysroot` and `toolchain_roots` need to be overriden by + `llvm_toolchain`. + """) + + _llvm_toolchain_files( + name = kwargs["name"] + "_files", + llvm_version = kwargs["llvm_version"], + ) + + _patch_dynamic_linker( + name = kwargs["name"] + "_patched_files", + dynamic_linker = linux_x86_64_sysroot_path + "/lib/ld-linux-x86-64.so.2", + library_paths = [ + "{sysroot}/{path}".format( + sysroot = linux_x86_64_sysroot_path, + path = path, + ) + for path in ["lib", "usr/lib"] + ], + base_workspace = "@{name}_files//".format(name = kwargs["name"]), + ) + + kwargs["sysroot"] = { + "linux-x86_64": linux_x86_64_sysroot_pkg, + } + + kwargs["toolchain_roots"] = { + "": "@{files}//".format( + files = kwargs["name"] + "_files", + ), + "linux-x86_64": "@{patched_files}//".format( + patched_files = kwargs["name"] + "_patched_files", + ), + } + + # https://github.com/bazelbuild/bazel/issues/4605 + patched_files_abspath = ( + BAZEL_EXTERNAL_DIR + "/" + kwargs["name"] + "_patched_files" + ) + + kwargs["link_libs"] = _extend_linux_x86_64_key( + kwargs.get("link_libs", {"": []}), + [ + # libraries from llvm are statically linked and we don't want to + # dynamically link duplicates from sysroot + "-nostdlib++", + "-fuse-ld={patched_files_abspath}/bin/ld.lld".format( + patched_files_abspath = patched_files_abspath, + ), + ] + [ + opt.format(sysroot = linux_x86_64_sysroot_path) + for opt in [ + "-Wl,--rpath={sysroot}/lib", + "-Wl,--rpath={sysroot}/usr/lib", + "-Wl,--rpath={sysroot}/../lib64", + "-Wl,--dynamic-linker={sysroot}/lib/ld-linux-x86-64.so.2", + ] + ], + ) + + major_version = kwargs["llvm_version"].split(".")[0] + kwargs["cxx_builtin_include_directories"] = _extend_linux_x86_64_key( + kwargs.get("cxx_builtin_include_directories", {"": []}), + [ + "{patched_files_abspath}/{include_dir}".format( + patched_files_abspath = patched_files_abspath, + include_dir = include_dir, + ) + for include_dir in [ + "/include/c++/v1", + "/include/x86_64-unknown-linux-gnu/c++/v1", + "/lib/clang/{}/include".format(major_version), + "/lib/clang/{}/share".format(major_version), + ] + ] + [ + # The clang-tidy aspect won't find include directories in the sysroot unless we + # specify the abspath. + # + # These *must* be found after C++ standard library headers: + # https://github.com/llvm/llvm-project/commit/8cedff10a18d8eba9190a645626fa6a509c1f139 + "{sysroot}/{include_dir}".format( + sysroot = linux_x86_64_sysroot_path, + include_dir = include_dir, + ) + for include_dir in [ + "include", + "usr/include", + "usr/local/include", + ] + ], + ) + + _llvm_toolchain(**kwargs) diff --git a/tools/llvm_toolchain_dependencies.bzl b/tools/llvm_toolchain_dependencies.bzl new file mode 100644 index 0000000..e5547fd --- /dev/null +++ b/tools/llvm_toolchain_dependencies.bzl @@ -0,0 +1,12 @@ +""" +Provides a wrapper around `bazel_toolchain_dependencies` which also obtains the +abspath to Bazel's external directory for use by the `llvm_toolchain` wrapper. +""" + +load("@bazel_toolchain//toolchain:deps.bzl", "bazel_toolchain_dependencies") +load("//tools:local_config_info.bzl", "local_config_info") + +# Rename to (hopefully) be less confusing +def llvm_toolchain_dependencies(): + bazel_toolchain_dependencies() + local_config_info(name = "local_config_info") diff --git a/tools/local_config_info.bzl b/tools/local_config_info.bzl new file mode 100644 index 0000000..aabc0f2 --- /dev/null +++ b/tools/local_config_info.bzl @@ -0,0 +1,20 @@ +""" +Repository rule for determining Bazel directories. +""" + +def _local_config_info_impl(rctx): + rctx.file("BUILD.bazel") + + external = str(rctx.path(".").realpath).removesuffix("/" + rctx.name) + + rctx.file( + "defs.bzl", + executable = False, + content = """BAZEL_EXTERNAL_DIR = "{}" + """.format(external), + ) + +local_config_info = repository_rule( + implementation = _local_config_info_impl, + doc = "A repository rule for determining Bazel directories", +)