Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ exports_files(
[
"LICENSE.md",
"NOTICE",
"presets.bzl",
],
visibility = ["//visibility:public"],
)
3 changes: 2 additions & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
module(name = "score_cpp_policies")

bazel_dep(name = "bazel_skylib", version = "1.8.2")
bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "aspect_rules_lint", version = "2.5.0")
bazel_dep(name = "bazelrc-preset.bzl", version = "1.9.2")
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Planned: clang-format, code coverage policies.

- **[`sanitizers/`](sanitizers/README.md)** — ASan/UBSan/LSan/TSan Bazel `cc_feature`s, ready-to-use `--config=` aliases, suppression files, and `target_compatible_with` constraints.
- **[`clang_tidy/`](clang_tidy/README.md)** — centralized `.clang-tidy` baseline (conservative, tailorable per module) and a `--config=clang-tidy` Bazel integration.
- **[`presets.bzl`](presets.bzl)** — sanitizers and clang-tidy as [bazelrc-preset.bzl](https://github.com/bazel-contrib/bazelrc-preset.bzl) presets, for consumers who'd rather generate their `.bazelrc` than hand-copy `sanitizers.bazelrc`/`clang_tidy.bazelrc`. See `sanitizers/README.md` § Distribution via bazelrc-preset.bzl.

## Sanitizers

Expand Down
15 changes: 15 additions & 0 deletions clang_tidy/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ exports_files(
".clang-tidy",
"clang_tidy.bazelrc",
"defs.bzl",
"presets.bzl",
],
visibility = ["//visibility:public"],
)

# Fails if clang_tidy.bazelrc and CLANG_TIDY_PRESETS drift apart.
sh_test(
name = "clang_tidy_bazelrc_equivalence_test",
srcs = ["//tools:check_bazelrc_equivalence.py"],
args = [
"$(location :clang_tidy.bazelrc)",
"$(location //tools:preset)",
],
data = [
":clang_tidy.bazelrc",
"//tools:preset",
],
)
6 changes: 6 additions & 0 deletions clang_tidy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ import %workspace%/path/to/clang_tidy.bazelrc # if vendored locally
> `tests/` self-test workspace here), a relative `import` works. Otherwise vendor
> the three lines from `clang_tidy.bazelrc` into your own `.bazelrc`.

> Or consume `CLANG_TIDY_PRESETS` via [bazelrc-preset.bzl](https://github.com/bazel-contrib/bazelrc-preset.bzl)
> instead — see `sanitizers/README.md` § Distribution via bazelrc-preset.bzl for the
> full steps. The same `tools/BUILD` preset target can pass
> `@score_cpp_policies//:presets.bzl`'s `PRESETS` (sanitizers + clang-tidy together) or
> `@score_cpp_policies//clang_tidy:presets.bzl`'s `CLANG_TIDY_PRESETS` alone.

### 3 — Create `tools/lint/BUILD.bazel`

```python
Expand Down
41 changes: 41 additions & 0 deletions clang_tidy/presets.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

"""bazelrc-preset.bzl extra_presets for the clang-tidy policy.

Mirrors the `test:clang-tidy` flags in clang_tidy.bazelrc, for consumers using
bazelrc-preset.bzl (https://github.com/bazel-contrib/bazelrc-preset.bzl) instead of
`import`.

The `aspects` value is a label relative to the consuming repo, not score_cpp_policies
— each consumer defines its own `tools/lint/linters.bzl` (see clang_tidy/README.md).
"""

# buildifier: keep-sorted
CLANG_TIDY_PRESETS = {
"aspects": struct(
command = "test:clang-tidy",
default = "//tools/lint:linters.bzl%clang_tidy_aspect",
description = "Run the consumer's clang-tidy aspect (see clang_tidy/README.md step 3-4) under --config=clang-tidy.",
),
"extra_toolchains": struct(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means they have to register all toolchains that they want to use on the project level? How then toolchain resolution is working? How this thing know which toolchain is selected?

command = "test:clang-tidy",
default = "@llvm_toolchain//:cc-toolchain-x86_64-linux",
description = "Use the LLVM toolchain registered by the consumer's llvm_toolchain extension for clang-tidy.",
),
"output_groups": struct(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that is then collected by filegroup output_group selection?

command = "test:clang-tidy",
default = "+rules_lint_report",
description = "Surface the rules_lint_report output group produced by the clang-tidy aspect.",
),
}
34 changes: 34 additions & 0 deletions presets.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

"""Single entry point for all score_cpp_policies bazelrc-preset.bzl presets.

Consumers wanting every policy this repo ships should load PRESETS:

load("@score_cpp_policies//:presets.bzl", "PRESETS")

bazelrc_preset(
name = "preset",
extra_presets = PRESETS,
)

Consumers wanting only one policy (e.g. sanitizers without clang-tidy) can load the
individual dicts instead: SANITIZER_PRESETS, CLANG_TIDY_PRESETS.

Coverage and CodeQL are not included here yet.
"""

load("//clang_tidy:presets.bzl", "CLANG_TIDY_PRESETS")
load("//sanitizers:presets.bzl", "SANITIZER_PRESETS")

PRESETS = SANITIZER_PRESETS | CLANG_TIDY_PRESETS
19 changes: 18 additions & 1 deletion sanitizers/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,27 @@
load("//sanitizers/private:expand_template.bzl", "expand_template")

exports_files(
["sanitizers.bazelrc"],
[
"presets.bzl",
"sanitizers.bazelrc",
],
visibility = ["//visibility:public"],
)

# Fails if sanitizers.bazelrc and SANITIZER_PRESETS drift apart.
sh_test(
name = "sanitizers_bazelrc_equivalence_test",
srcs = ["//tools:check_bazelrc_equivalence.py"],
args = [
"$(location :sanitizers.bazelrc)",
"$(location //tools:preset)",
],
data = [
":sanitizers.bazelrc",
"//tools:preset",
],
)

filegroup(
name = "suppressions",
srcs = [
Expand Down
37 changes: 37 additions & 0 deletions sanitizers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,43 @@ bazel test --config=tsan_ubsan //your/target/...
> ```
> try-import %workspace%/path/to/score_cpp_policies/sanitizers/sanitizers.bazelrc
> ```
>
> Or consume `SANITIZER_PRESETS` via [bazelrc-preset.bzl](https://github.com/bazel-contrib/bazelrc-preset.bzl) instead — see
> [Distribution via bazelrc-preset.bzl](#distribution-via-bazelrc-presetbzl) below.

---

## Distribution via bazelrc-preset.bzl

[bazelrc-preset.bzl](https://github.com/bazel-contrib/bazelrc-preset.bzl) generates a
`.bazelrc` fragment from `SANITIZER_PRESETS` that you vendor into your own repo,
instead of copy-pasting `sanitizers.bazelrc` by hand.

1. Add the dependency to your `MODULE.bazel`:
```python
bazel_dep(name = "score_cpp_policies", version = "<version>")
bazel_dep(name = "bazelrc-preset.bzl", version = "1.9.2")
```
2. Reference `SANITIZER_PRESETS` (or `PRESETS` for sanitizers + clang-tidy together)
from a `BUILD` file, e.g. `tools/BUILD`:
```python
load("@bazelrc-preset.bzl", "bazelrc_preset")
load("@score_cpp_policies//:presets.bzl", "PRESETS")
# Or, sanitizers only:
# load("@score_cpp_policies//sanitizers:presets.bzl", "SANITIZER_PRESETS")

bazelrc_preset(
name = "preset",
extra_presets = PRESETS,
)
```
3. Generate and commit the preset file: `bazel run //tools:preset.update`.
4. Import it from your workspace `.bazelrc`:
```
import %workspace%/tools/preset.bazelrc
```
5. `bazel test //tools:preset.update_test` catches drift whenever `presets.bzl`
changes — re-run step 3 to update.

---

Expand Down
183 changes: 183 additions & 0 deletions sanitizers/presets.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

"""bazelrc-preset.bzl extra_presets for the sanitizers policy.

Mirrors the --config=asan/ubsan/lsan/tsan/asan_ubsan_lsan/tsan_ubsan flags in
sanitizers.bazelrc, for consumers using bazelrc-preset.bzl
(https://github.com/bazel-contrib/bazelrc-preset.bzl) instead of `import`.
"""

_WRAPPER = "@score_cpp_policies//sanitizers:wrapper"

# buildifier: keep-sorted
SANITIZER_PRESETS = {
"@score_cpp_policies//sanitizers/flags:asan": struct(
command = "build:asan",
default = True,
description = "Activate the ASan cc_feature (score_asan) for the //sanitizers/flags:asan_on config_setting.",
),
"@score_cpp_policies//sanitizers/flags:lsan": struct(
command = "build:lsan",
default = True,
description = "Activate the LSan cc_feature (score_lsan) for the //sanitizers/flags:lsan_on config_setting.",
),
"@score_cpp_policies//sanitizers/flags:tsan": struct(
command = "build:tsan",
default = True,
description = "Activate the TSan cc_feature (score_tsan) for the //sanitizers/flags:tsan_on config_setting.",
),
"@score_cpp_policies//sanitizers/flags:ubsan": struct(
command = "build:ubsan",
default = True,
description = "Activate the UBSan cc_feature (score_ubsan) for the //sanitizers/flags:ubsan_on config_setting.",
),
"config": [
struct(
command = "build:asan",
default = "with_debug_symbols",
description = "Keep debug symbols (-g1, --strip=never) so sanitizer stack traces are readable.",
),
struct(
command = "build:ubsan",
default = "with_debug_symbols",
description = "Keep debug symbols (-g1, --strip=never) so sanitizer stack traces are readable.",
),
struct(
command = "build:lsan",
default = "with_debug_symbols",
description = "Keep debug symbols (-g1, --strip=never) so sanitizer stack traces are readable.",
),
struct(
command = "build:tsan",
default = "with_debug_symbols",
description = "Keep debug symbols (-g1, --strip=never) so sanitizer stack traces are readable.",
),
struct(
command = "build:asan_ubsan_lsan",
default = "asan",
allow_repeated = True,
description = "Composite config: address + leak + UB checking together.",
),
struct(
command = "build:asan_ubsan_lsan",
default = "ubsan",
allow_repeated = True,
description = "Composite config: address + leak + UB checking together.",
),
struct(
command = "build:asan_ubsan_lsan",
default = "lsan",
allow_repeated = True,
description = "Composite config: address + leak + UB checking together.",
),
struct(
command = "build:tsan_ubsan",
default = "tsan",
allow_repeated = True,
description = "Composite config: thread + UB checking together.",
),
struct(
command = "build:tsan_ubsan",
default = "ubsan",
allow_repeated = True,
description = "Composite config: thread + UB checking together.",
),
],
"features": [
struct(
command = "test:with_debug_symbols",
default = "debug_symbols",
description = "Emit -g1 debug info under any sanitizer config so stack traces resolve symbols.",
),
struct(
command = "build:asan",
default = "score_asan",
description = "Compile/link with -fsanitize=address via the score_asan cc_feature.",
),
struct(
command = "build:ubsan",
default = "score_ubsan",
description = "Compile/link with -fsanitize=undefined via the score_ubsan cc_feature.",
),
struct(
command = "build:lsan",
default = "score_lsan",
description = "Compile/link with -fsanitize=leak via the score_lsan cc_feature.",
),
struct(
command = "build:tsan",
default = "score_tsan",
description = "Compile/link with -fsanitize=thread via the score_tsan cc_feature.",
),
],
"platform_suffix": [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? This will duplicate build tree for each configuration. Is there a reason why we do this?

struct(
command = "build:asan",
default = "asan",
description = "Disambiguate the asan output tree from plain builds.",
),
struct(
command = "build:ubsan",
default = "ubsan",
description = "Disambiguate the ubsan output tree from plain builds.",
),
struct(
command = "build:lsan",
default = "lsan",
description = "Disambiguate the lsan output tree from plain builds.",
),
struct(
command = "build:tsan",
default = "tsan",
description = "Disambiguate the tsan output tree from plain builds.",
),
struct(
command = "build:asan_ubsan_lsan",
default = "asan_ubsan_lsan",
description = "Canonical suffix for the composite asan+ubsan+lsan config, overriding the per-sanitizer suffixes.",
),
struct(
command = "build:tsan_ubsan",
default = "tsan_ubsan",
description = "Canonical suffix for the composite tsan+ubsan config, overriding the per-sanitizer suffixes.",
),
],
"run_under": [
struct(
command = "test:asan",
default = _WRAPPER,
description = "Run tests under the sanitizers wrapper so suppressions/options env files are applied.",
),
struct(
command = "test:ubsan",
default = _WRAPPER,
description = "Run tests under the sanitizers wrapper so suppressions/options env files are applied.",
),
struct(
command = "test:lsan",
default = _WRAPPER,
description = "Run tests under the sanitizers wrapper so suppressions/options env files are applied.",
),
struct(
command = "test:tsan",
default = _WRAPPER,
description = "Run tests under the sanitizers wrapper so suppressions/options env files are applied.",
),
],
"strip": struct(
command = "build:with_debug_symbols",
default = "never",
description = "Never strip symbols under any sanitizer config so stack traces remain readable.",
),
}
Loading
Loading