This commit is contained in:
parent
a25b0a1ded
commit
8892154b28
70 changed files with 2780 additions and 596 deletions
63
README.md
63
README.md
|
|
@ -245,6 +245,67 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view>)
|
|||
|
||||
`ParentLib(name)` walks the parent project's dependency graph (and the parent itself) to find a `Configuration*` by `Configuration::name`. The build links it into the test exe, so `import MyLib;` resolves naturally. See [examples/tests/](examples/tests/) for the complete worked example.
|
||||
|
||||
## Linting & formatting
|
||||
|
||||
Lint rules are C++ in `project.cpp` — no `.lintrc`, no YAML, and no built-in rules. A rule is a named callback that runs once per source file; register it on the `Configuration` before returning:
|
||||
|
||||
```cpp
|
||||
cfg.AddLintRule("no-tabs", [](Crafter::LintContext& ctx) {
|
||||
if (ctx.Extension() != ".cpp" && ctx.Extension() != ".cppm") return;
|
||||
for (std::size_t n = 1; n <= ctx.lines.size(); ++n) {
|
||||
if (ctx.Line(n).contains('\t')) ctx.Report(n, "tab character (use spaces)");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
A rule that calls `ctx.SetContent(newContent)` is a **transform** — defined once, it powers both verbs: `lint` reports where the transform would change the file (dry, never writes), and `format` applies it to disk:
|
||||
|
||||
```cpp
|
||||
cfg.AddLintRule("final-newline", [](Crafter::LintContext& ctx) {
|
||||
if (!ctx.content.empty() && !ctx.content.ends_with('\n')) {
|
||||
ctx.SetContent(ctx.content + '\n');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```bash
|
||||
crafter-build lint # full dry gate: Report() findings + would-reformat, exit 1 on any
|
||||
crafter-build lint 'spdx*' # rule-name glob filter
|
||||
crafter-build lint --list # enumerate rules without running them
|
||||
crafter-build format # apply transforms, rewrite changed files in place (exit 0)
|
||||
crafter-build format --check # CI gate: list files that would change, exit 1 if any
|
||||
```
|
||||
|
||||
Lint findings print compiler-style — `file:line: warning: message [rule]` — so editors and CI pick them up. `format` rewrites files in place (gofmt convention) and only touches files whose bytes actually change; report-only findings are `lint`'s business and don't affect `format`'s exit code. A project with no rules registered exits 1 with a message showing how to add one.
|
||||
|
||||
`LintContext` gives each rule:
|
||||
|
||||
| Member | Meaning |
|
||||
|---|---|
|
||||
| `file` | absolute path of the file under lint |
|
||||
| `content` | the whole file |
|
||||
| `lines` | pre-split line views (report 1-based via `Line(n)`) |
|
||||
| `Extension()` | file extension, e.g. `".cppm"` |
|
||||
| `CommentStripped()` | `content` with comments and string/char literal bodies blanked to spaces — newlines kept, so line numbers stay valid (raw strings not recognized yet) |
|
||||
| `Report(line, msg)` | record a finding (`line == 0` for whole-file) |
|
||||
| `SetContent(str)` | replace the file's content — makes the rule a transform; `lines` re-splits, `CommentStripped()` re-derives. Build the new string, call once at the end; don't assign `content` directly |
|
||||
|
||||
Suppression is comment-based :
|
||||
|
||||
```cpp
|
||||
// lint-disable-next-line no-char-pointer — one rule, the following line
|
||||
// lint-disable-next-line naming, wrap-join — several rules (space- or comma-separated)
|
||||
// lint-disable-next-line — all rules, the following line
|
||||
// lint-disable-file fixed-width-types — one rule, whole file (any position)
|
||||
// lint-disable-file — all rules, whole file
|
||||
```
|
||||
|
||||
Directives silence findings **and** stop `format` from rewriting the suppressed lines. The driver handles this automatically for reports and line-preserving transforms; a custom transform that merges or splits lines must ask `ctx.Suppressed(rule, line)` for the lines it touches (the built-in house rules do).
|
||||
|
||||
The linted file set is the project's own sources: module interfaces (+ partitions), implementations, `cFiles`, `cuda`, shader sources, declared tests' sources, and `project.cpp` itself — for the root `Configuration` plus every transitive dependency whose path lies inside the project root. `GitProject` / external dependencies are foreign code and are skipped, as are `files`/`buildFiles`/`assets` (data, not source). Rules registered on any in-root config are collected and deduplicated by name, so with a lib/exe split you can attach them to either. Rules run in registration order, each seeing the previous transform's output; a rule that throws is reverted and becomes a finding instead of aborting the run. Files are read and written as bytes — line views carry a trailing `\r` on CRLF files, so transforms that should be CRLF-safe must handle it (see the `trim-trailing-ws` dogfood rule).
|
||||
|
||||
This repo dogfoods the feature with its full house style — 15 rules in [lint-rules.h](lint-rules.h) (naming conventions, K&R braces, `enum class`, `std::println`, fixed-width integer types, `std::format` over concatenation, wrap-joining, whitespace hygiene), registered by [project.cpp](project.cpp) via one `ProjectLint::AddProjectLintRules(cfg)` call. The header is deliberately project-local, not part of the crafter-build library — copy it into a sibling repo to adopt the same style there; `tests/HouseRules/` validates every transform. Most rules are transforms: `crafter-build format` mechanically restyled this entire codebase (~150 fixes), with only expression rewrites the operand scanner can't prove safe (ternaries, raw-string lines) reported for hand-fixing.
|
||||
|
||||
## Examples
|
||||
|
||||
[examples/](examples/) contains progressively larger self-contained projects:
|
||||
|
|
@ -260,7 +321,7 @@ Each builds standalone: `cd examples/<name> && crafter-build`.
|
|||
|
||||
## Architecture
|
||||
|
||||
- **Modules**: `Crafter.Build:Shader` / `:Platform` / `:Interface` / `:Implementation` / `:External` / `:Clang` / `:Test` partitions, re-exported by the `Crafter.Build` umbrella.
|
||||
- **Modules**: `Crafter.Build:Shader` / `:Platform` / `:Interface` / `:Implementation` / `:External` / `:Clang` / `:Test` / `:Lint` / `:Progress` / `:Asset` partitions, re-exported by the `Crafter.Build` umbrella.
|
||||
- **Build process**: parallel — interface PCMs, implementation `.o` files, external dep clones, dep-config recursive builds, and shader compilations all spawn threads, sync at well-defined join points.
|
||||
- **`LoadProject`** compiles `project.cpp` to a shared object (`.so` on Linux, `.dll` on Windows) and loads it. On Linux the host exe is linked with `-Wl,--export-dynamic` so the project's undefined symbols resolve back into the running binary. On Windows the host is split into `crafter-build.dll` (everything) + a thin `crafter-build.exe` launcher; the public `Crafter::*` API is annotated with a `CRAFTER_API` macro (`__declspec(dllexport)` when the host is built, `__declspec(dllimport)` when project.dll consumes it via the rebuilt user-cache PCMs), and the host passes `-Wl,/EXPORT:CrafterBuildProject` when linking project.dll so user `project.cpp` files don't need platform-gated annotations.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue