Skip to content

Commit a3659e4

Browse files
Initial cpp-tools version (#1)
* Initial cpp-tools version * Standardized spot for clang configuration, helper/CI scripts, AGENTS.md C++ guidance, and pre-commit hook * Moves clang-tidy check documentation out of Notion into this repository * Tested against multiple repositories to ensure correctness
1 parent 1ea84e8 commit a3659e4

12 files changed

Lines changed: 1460 additions & 1 deletion

File tree

.clang-format

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
BasedOnStyle: Google
3+
Language: Cpp
4+
Standard: c++17
5+
6+
# LiveKit modifications to Google style below
7+
8+
ColumnLimit: 120 # more width on modern screens
9+
SpacesBeforeTrailingComments: 1 # one space for trailing namespace comments, e.g. `} // namespace foo` (Google uses 2)
10+
AccessModifierOffset: -2 # left-align public/protected/private with the class keyword (Google indents by 1)

.clang-tidy

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
Checks: >
2+
-*,
3+
clang-analyzer-*,
4+
bugprone-*,
5+
misc-const-correctness,
6+
performance-*,
7+
modernize-*,
8+
readability-misleading-indentation,
9+
readability-redundant-smartptr-get,
10+
readability-identifier-naming,
11+
-bugprone-easily-swappable-parameters,
12+
-modernize-use-trailing-return-type,
13+
-modernize-avoid-c-arrays,
14+
-modernize-type-traits,
15+
-modernize-use-auto,
16+
-modernize-use-nodiscard,
17+
-modernize-return-braced-init-list,
18+
-performance-enum-size,
19+
-readability-braces-around-statements,
20+
21+
# These warnings have determined to be critical and are as such treated as errors
22+
WarningsAsErrors: >
23+
clang-analyzer-*,
24+
bugprone-use-after-move,
25+
bugprone-dangling-handle,
26+
bugprone-infinite-loop,
27+
bugprone-narrowing-conversions,
28+
bugprone-undefined-memory-manipulation,
29+
bugprone-move-forwarding-reference,
30+
bugprone-incorrect-roundings,
31+
bugprone-sizeof-expression,
32+
bugprone-string-literal-with-embedded-nul,
33+
bugprone-suspicious-memset-usage,
34+
35+
FormatStyle: file
36+
37+
CheckOptions:
38+
- key: modernize-use-nullptr.NullMacros
39+
value: 'NULL'
40+
- key: readability-identifier-naming.ClassCase
41+
value: CamelCase
42+
- key: readability-identifier-naming.MethodCase
43+
value: camelBack

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @alan-george-lk @stephen-derosa

.github/workflows/ci.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: CI
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
push:
7+
branches: ["main"]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
lint:
14+
name: Lint
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 10
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
21+
22+
- name: Install ShellCheck
23+
run: |
24+
sudo apt-get update
25+
sudo apt-get install -y shellcheck
26+
27+
- name: Check shell scripts
28+
run: |
29+
bash -n ./*.sh
30+
shellcheck ./*.sh
31+
32+
- name: Check Markdown
33+
run: npx --yes markdownlint-cli2@0.23.1 README.md AGENTS.md 'docs/**/*.md'
34+
35+
link-check:
36+
name: Link Check
37+
runs-on: ubuntu-latest
38+
timeout-minutes: 10
39+
40+
steps:
41+
- name: Checkout
42+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
43+
44+
- name: Restore lychee cache
45+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
46+
with:
47+
path: .lycheecache
48+
key: cache-lychee-${{ github.sha }}
49+
restore-keys: cache-lychee-
50+
51+
- name: Run lychee
52+
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
53+
with:
54+
args: >-
55+
--verbose
56+
--no-progress
57+
--cache
58+
--max-cache-age 1d
59+
--root-dir .
60+
README.md
61+
AGENTS.md
62+
'docs/**/*.md'
63+
fail: true
64+
jobSummary: false

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.lycheecache

.markdownlint-cli2.jsonc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"config": {
3+
"MD013": false
4+
}
5+
}

AGENTS.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# AGENTS.md — Shared C++ Engineering Baseline
2+
3+
## Scope
4+
5+
These rules apply to LiveKit C++ projects that consume `cpp-tools`. If a consuming
6+
repository has an `AGENTS.md` with conflicting rules they should take priority.
7+
8+
## Safety and Determinism
9+
10+
- Design for predictable memory use, execution time, and failure behavior.
11+
- Avoid heap allocation when practical. In real-time, callback, and steady-state
12+
paths, allocate resources during initialization and reuse them.
13+
- Prefer stack storage, RAII, fixed-capacity storage, and bounded pools or
14+
queues. Document unavoidable dynamic allocation in time-sensitive code.
15+
- Put explicit bounds on queues, retries, buffers, worker counts, and waits.
16+
Define observable behavior for exhaustion and overload.
17+
- Do not block real-time or callback threads with file/network I/O, sleeps,
18+
unbounded work, allocation, or contended locks. Offload work through bounded
19+
mechanisms with clear back-pressure.
20+
- Avoid unbounded recursion and large stack objects. Account for stack limits on
21+
embedded targets.
22+
23+
## Errors and Failure Modes
24+
25+
- Use return values for expected failures instead of exceptions:
26+
- `std::optional<T>` when absence is expected and needs no diagnostic.
27+
- `bool` for a simple success/failure result.
28+
- `Result<T, E>`, `expected` (C++23 or higher), or equivalent when callers need a typed error.
29+
- Callers must inspect status-bearing return values. Mark important results
30+
`[[nodiscard]]` where practical.
31+
- Do not throw through C, FFI, callback, destructor, real-time, or
32+
resource-constrained boundaries.
33+
- Reserve exceptions for genuinely exceptional failures when the consuming
34+
project permits them. Catch them at a well-defined boundary and convert them
35+
to the project's error model.
36+
- Validate external inputs and cross-boundary data. Keep state valid on failure
37+
and prefer fail-safe behavior over partial updates.
38+
39+
## Memory, Ownership, and Lifetime
40+
41+
- Make ownership explicit. Prefer values and RAII types; do not use raw owning
42+
pointers.
43+
- Use `std::unique_ptr` for exclusive dynamic ownership and `std::shared_ptr`
44+
only when ownership is genuinely shared.
45+
- Avoid heap allocations as much as possible. If code is heap allocating in a loop
46+
or a high-frequency path, second guess the design and consider alternatives.
47+
- Keep object lifetimes and teardown order deterministic. Destructors must not
48+
throw.
49+
- Avoid hidden copies of large buffers. Make copy and move behavior intentional,
50+
especially for media, sensor, and message data.
51+
- Declare data at the smallest useful scope and initialize it before use.
52+
53+
## Types, Arithmetic, and Units
54+
55+
- Prefer STL types over third-party dependencies when possible.
56+
- Prefer fixed-width integers from `<cstdint>` when width or signedness matters,
57+
including serialization, FFI, hardware, timestamps, IDs, and public APIs.
58+
- Use platform-sized primitive integers only when the value is intentionally
59+
platform-sized or compatibility requires it.
60+
- Avoid implicit narrowing and mixed signed/unsigned arithmetic. Validate ranges
61+
before conversions and arithmetic that can overflow.
62+
- Represent durations and time points with `std::chrono`; use a monotonic clock
63+
for elapsed time, deadlines, and timeouts.
64+
- Make physical units explicit with strong or clearly named types, such as `_us`
65+
for microseconds. Do not pass ambiguous raw numeric values across interfaces.
66+
67+
## Concurrency
68+
69+
- Document which threads call an API and whether each type is thread-safe.
70+
- Minimize shared mutable state. Protect it with clear synchronization and keep
71+
critical sections short.
72+
- Never call user code while holding an internal lock.
73+
- Use bounded waits and define cancellation and shutdown behavior. Join worker
74+
threads outside locks.
75+
- Treat atomics and lock-free code as specialized tools; document memory-order
76+
reasoning and test concurrency paths under stress.
77+
78+
## Design and Readability
79+
80+
- Keep functions focused and short, roughly 60 lines or fewer when practical.
81+
- Prefer straightforward control flow over clever abstractions. Document any
82+
deliberate tradeoff between readability, determinism, and performance.
83+
- Use `enum class`, `nullptr`, explicit conversions, and const-correct
84+
interfaces.
85+
- Check non-void return values and make ignored results explicit.
86+
- Use `git mv` when moving or renaming tracked files.
87+
88+
## Portability
89+
90+
- Avoid undefined behavior, compiler-specific assumptions, and dependence on
91+
host endianness, alignment, or primitive widths.
92+
- Keep cross-platform and cross-architecture boundaries explicit. Test all
93+
supported targets defined by the consuming repository.
94+
- Keep third-party implementation details out of public headers and ABI
95+
boundaries.
96+
97+
## Style
98+
99+
- Add the LiveKit copyright header with the correct year to new code files.
100+
- Prefer the constructor initializer list rather than variable declaration
101+
and assignment in the constructor body.
102+
- For Doxygen/doc comments, prefer `///` comment style and use @brief,
103+
@param, @return, @throw, @ref, @note, @warning as applicable.
104+
105+
## Project-Owned clangd Configuration
106+
107+
- Each consuming project must provide its own `.clangd`; compilation database
108+
locations and flags are project-specific and are not shared by `cpp-tools`.
109+
- Verify `.clangd` points clangd at the project's generated compilation database
110+
before relying on IDE diagnostics.
111+
- clang-tidy does not read `.clangd`. Before running clang-tidy, generate a valid
112+
`compile_commands.json` and pass its build directory to `clang-tidy.sh`.
113+
114+
## Verification
115+
116+
- Adhere to the shared `.clang-format` and `.clang-tidy` configurations.
117+
- After C++ changes, run `./cpp-tools/clang-format.sh` with the consuming
118+
project's paths. Use `--fix` when needed, then rerun the check.
119+
- Generate the consuming project's compilation database and run
120+
`./cpp-tools/clang-tidy.sh` with its documented build directory and filters.
121+
- Do not bypass formatter or static-analysis failures. Keep suppressions narrow,
122+
local, and justified in code.
123+
- Add deterministic tests for normal, boundary, overload, timeout, cancellation,
124+
and failure behavior. Avoid timing-only sleeps when a condition or simulated
125+
clock can be used.
126+
- Benchmark or stress-test new time-sensitive or resource-sensitive behavior and
127+
verify that configured limits are enforced.

README.md

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,138 @@
11
# cpp-tools
2-
Standardized set of tools for use in LiveKit C++ projects.
2+
3+
Standardized tools for LiveKit C++ projects. This repository provides:
4+
5+
- [clang-format](https://clang.llvm.org/docs/ClangFormat.html): Code styling consistency across projects
6+
- [clang-tidy](https://clang.llvm.org/extra/clang-tidy/): Standardized static analysis and bug catching checks. See [docs/clang-tidy.md](./docs/clang-tidy.md)
7+
- Base [AGENTS.md](./AGENTS.md) with C++ best practices
8+
- Helper scripts and GitHub actions reporting support
9+
10+
This repository is intended to be consumed as a git submodule.
11+
12+
## Quick start
13+
14+
Add this repository as a submodule from the consuming repository root:
15+
16+
```bash
17+
git submodule add https://github.com/livekit/cpp-tools.git cpp-tools
18+
```
19+
20+
Install the shared configuration symlinks:
21+
22+
```bash
23+
./cpp-tools/install.sh # Installs .clang-format and .clang-tidy symlinks to repo root
24+
```
25+
26+
Optionally install a precommit hook that automatically runs `clang-format` before commits:
27+
28+
```bash
29+
./cpp-tools/install.sh precommit-hook # Installs precommit hook
30+
```
31+
32+
Run the tools from the repository root:
33+
34+
```bash
35+
# Check formatting or rewrite files in place.
36+
./cpp-tools/clang-format.sh --path path/to/sources
37+
./cpp-tools/clang-format.sh --path path/to/sources --fix
38+
39+
# Run static analysis after generating compile_commands.json.
40+
./cpp-tools/clang-tidy.sh --file-regex '.*\.(c|cpp|cc|cxx)$'
41+
./cpp-tools/clang-tidy.sh --file-regex '.*\.(c|cpp|cc|cxx)$' --fail-on-warning
42+
```
43+
44+
> Note: It's recommended to wrap these tool scripts in consuming repository scripts such that arguments and options can be passed in and re-used in CI. See below section for examples.
45+
46+
Update existing `AGENTS.md` file to reference this one:
47+
48+
```markdown
49+
## Shared C++ baseline
50+
Follow `cpp-tools/AGENTS.md` for shared LiveKit C++ engineering guidance.
51+
Instructions in this file are project-specific and take precedence if they
52+
conflict with the shared baseline.
53+
```
54+
55+
## clang-format
56+
57+
Each consumer supplies the tracked paths that `clang-format.sh` should check.
58+
Pass `--path` repeatedly for repositories with multiple source trees:
59+
60+
```bash
61+
./cpp-tools/clang-format.sh \
62+
--path path/to/sources \
63+
--path path/to/headers
64+
```
65+
66+
The `CLANG_FORMAT_PATHS` environment variable provides the same configuration
67+
as a colon-separated list. Positional file paths restrict the check to those
68+
files.
69+
70+
## clang-tidy
71+
72+
See [docs/clang-tidy.md](docs/clang-tidy.md) for the enabled checks,
73+
exclusions, and the reasoning behind them.
74+
75+
The consuming repository must generate `compile_commands.json` before running
76+
`clang-tidy.sh`. Project-specific behavior is configured with command-line
77+
flags or their corresponding environment variables:
78+
79+
```bash
80+
./cpp-tools/clang-tidy.sh \
81+
--build-dir build-release \
82+
--file-regex '.*\.(c|cpp|cc|cxx)$'
83+
```
84+
85+
Additional `run-clang-tidy` arguments can be passed after `--`.
86+
87+
## Tool wrappers
88+
89+
Consuming repositories should provide thin project-owned entrypoints such as
90+
`scripts/clang-format.sh` and `scripts/clang-tidy.sh`. The wrappers encode the
91+
repository's paths, filters, and build directory, then `exec` the shared script.
92+
This gives developers a zero-argument command without copying the formatting,
93+
diagnostic, or GitHub summary implementation.
94+
95+
For example:
96+
97+
```bash
98+
#!/usr/bin/env bash
99+
set -euo pipefail
100+
101+
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
102+
export CLANG_FORMAT_FIX_COMMAND="./scripts/clang-format.sh --fix"
103+
exec "${repo_root}/cpp-tools/clang-format.sh" \
104+
--repo-root "${repo_root}" \
105+
--path src \
106+
--path include \
107+
"$@"
108+
```
109+
110+
Repository-owned CI workflows should invoke the same project entrypoints so
111+
local and CI file selection cannot drift. Repositories that do not use wrappers
112+
can call the shared scripts with explicit arguments.
113+
114+
## Pre-commit hook
115+
116+
The hook formats staged C++ files and re-stages files rewritten by `clang-format`.
117+
118+
## GitHub Actions
119+
120+
The scripts automatically enable GitHub annotations and step summaries when
121+
`GITHUB_ACTIONS=true`. Consumer repositories own checkout, tool installation,
122+
and project-specific build preparation, then call their project wrappers:
123+
124+
```yaml
125+
- name: Run clang-format
126+
env:
127+
FORMAT_BLOB_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
128+
run: ./scripts/clang-format.sh
129+
130+
- name: Run clang-tidy
131+
env:
132+
TIDY_BLOB_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
133+
run: ./scripts/clang-tidy.sh --fail-on-warning
134+
```
135+
136+
`FORMAT_BLOB_SHA` and `TIDY_BLOB_SHA` make source links target the pull
137+
request's head commit such that links to violating files render correctly.
138+
The scripts fall back to `GITHUB_SHA` when these values are not supplied.

0 commit comments

Comments
 (0)