# Processors
RSConstruct uses **processors** to discover and build products. Each processor scans for source files matching its conventions and produces output files.
## Processor Types
There are four processor types: checker, generator, explicit, and mass generator. They differ in how inputs are discovered, how outputs are declared, and how results are cached.
See [Processor Types](processor-types.md) for full descriptions, examples, and a comparison table.
## Configuration
Declare processors by adding `[processor.TYPE.NAME]` sections to `rsconstruct.toml`:
```toml
[processor.checker.ruff]
[processor.checker.pylint]
args = ["--disable=C0114"]
[processor.generator.cc_single_file]
```
Only declared processors run — no processors are enabled by default. Use `rsconstruct smart auto` to auto-detect and add relevant processors.
Use `rsconstruct processor list` to see declared processors and descriptions.
Use `rsconstruct processor list --all` to show all built-in processors, not just those enabled in the project.
Use `rsconstruct processor files` to see which files each processor discovers.
## Available Processors
- [Tera](processor/generator/tera.md) — renders Tera templates into output files
- [Ruff](processor/checker/ruff.md) — lints Python files with ruff
- [Pylint](processor/checker/pylint.md) — lints Python files with pylint
- [Mypy](processor/checker/mypy.md) — type-checks Python files with mypy
- [Pyrefly](processor/checker/pyrefly.md) — type-checks Python files with pyrefly
- [CC](processor/mass_generator/cc.md) — builds full C/C++ projects from cc.yaml manifests
- [CC Single File](processor/generator/cc_single_file.md) — compiles C/C++ source files into executables (single-file)
- [Linux Module](processor/mass_generator/linux_module.md) — builds Linux kernel modules from linux-module.yaml manifests
- [Cppcheck](processor/checker/cppcheck.md) — runs static analysis on C/C++ source files
- [Clang-Tidy](processor/checker/clang_tidy.md) — runs clang-tidy static analysis on C/C++ source files
- [Shellcheck](processor/checker/shellcheck.md) — lints shell scripts using shellcheck
- [Zspell](processor/checker/zspell.md) — checks documentation files for spelling errors
- [Rumdl](processor/checker/rumdl.md) — lints Markdown files with rumdl
- [Oxlint](processor/checker/oxlint.md) — lints JavaScript/TypeScript files with oxlint
- [Selene](processor/checker/selene.md) — lints Lua files with selene
- [Iluacheck](processor/checker/iluacheck.md) — lints Lua files with luacheck's analysis (native)
- [Biome](processor/checker/biome.md) — lints CSS/JavaScript/TypeScript/JSON files with biome
- [Make](processor/checker/make.md) — runs make in directories containing Makefiles
- [Cargo](processor/mass_generator/cargo.md) — builds Rust projects using Cargo
- [Yamllint](processor/checker/yamllint.md) — lints YAML files with yamllint
- [Jq](processor/checker/jq.md) — validates JSON files with jq
- [Jsonlint](processor/checker/jsonlint.md) — lints JSON files with jsonlint
- [Taplo](processor/checker/taplo.md) — checks TOML files with taplo
- [Terms](processor/checker/terms.md) — checks that technical terms are backtick-quoted in Markdown files
- [Json Schema](processor/checker/json_schema.md) — validates JSON schema propertyOrdering
- [Iyamlschema](processor/checker/iyamlschema.md) — validates YAML files against JSON schemas (native)
- [Yaml2json](processor/generator/yaml2json.md) — converts YAML files to JSON (native)
- [Markdown2html](processor/generator/markdown2html.md) — converts Markdown to HTML using markdown CLI
- [Imarkdown2html](processor/generator/imarkdown2html.md) — converts Markdown to HTML (native)
- [Mass Generator](processor/mass_generator/generic.md) — runs a tool that enumerates its outputs in advance, one cached product per predicted file
- [Zola](processor/mass_generator/zola.md) — builds a zola site, one cached product per file zola writes, planned natively
## Clean behavior
`rsconstruct clean outputs` calls each product's processor-defined `clean()`. What gets removed depends on the processor type:
| Checker | Nothing — checkers produce no outputs | — |
| Generator | Each declared output file (`product.outputs`) | No |
| Explicit | Declared `output_files` + declared `output_dirs` | Yes (for `output_dirs` only) |
| Mass generator | Each predicted output file (`product.outputs`) | No |
Recursive directory removal is reserved for the user-declared `output_dirs` of Explicit. All other processor types remove individually-declared files and nothing else. A mass generator's `output_dirs` only bound where its predicted files may live; the directory itself is never removed directly, though the empty-directory sweep removes directories that become empty.
After every product's `clean()` completes, the orchestrator runs an empty-directory sweep: every parent of every removed output (and every parent of every removed `output_dir`) is tried with `fs::remove_dir` (non-recursive — succeeds only on already-empty directories), walking upward until a non-empty directory or the project root. The sweep does not special-case `out/` — any ancestor of a cleaned output is eligible.
See [`rsconstruct clean`](commands.md#rsconstruct-clean) for the full command reference, the `--no-empty-dirs` flag, the `-p` filter, and the other clean variants (`all`, `git`, `unknown`).
### Worked example
Consider this project:
```toml
# rsconstruct.toml
[processor.checker.ruff] # Checker — no outputs
src_dirs = ["src"]
[processor.generator.tera] # Generator — declared file outputs
src_dirs = ["templates"]
src_extensions = [".tera"]
[processor.mass_generator.mdbook] # Mass generator — planned file outputs
src_dirs = ["docs"]
```
Layout:
```text
project/
├── rsconstruct.toml
├── src/main.py # ruff lints this
├── templates/index.html.tera # tera renders to → out/processor.generator.tera/index.html
├── docs/
│ ├── book.toml
│ └── src/SUMMARY.md
└── book/ # mdbook output_dir; each file is a planned product
├── index.html
├── css/general.css
└── ...
```
After `rsconstruct build`, both `out/processor.generator.tera/index.html` and the planned files under `book/` exist.
Now run `rsconstruct clean outputs`:
1. **ruff** — Checker. `clean()` is a no-op. Nothing removed.
2. **tera** — Generator. `clean()` calls `fs::remove_file("out/processor.generator.tera/index.html")`. The file goes; `out/processor.generator.tera/` and `out/` are not yet touched.
3. **mdbook** — Mass generator. `clean()` removes each planned output file under `book/`.
4. **Empty-directory sweep.** The orchestrator collects parents of every removed path (`out/processor.generator.tera`, `out`, `book/css`, `book`) and removes those that are now empty, deepest-first.
Final state: `src/main.py`, `templates/index.html.tera`, `docs/...`, and `rsconstruct.toml` remain untouched. The cache (`.rsconstruct/`) is untouched. `out/` and `book/` are gone.
If you instead ran `rsconstruct clean outputs --no-empty-dirs`, the only difference is step 4 is skipped — `out/processor.generator.tera/` and `out/` would remain as empty directories.
If you ran `rsconstruct clean outputs -p processor.generator.tera`, only step 2 + the sweep of its parents runs. `book/` is left intact because mdbook is filtered out.
## Custom Processors
A command of your own runs through a config-driven processor: `[processor.checker.script]` for a check, `[processor.generator.generic]` for one output per input, `[processor.explicit.generic]` for declared inputs and outputs, and `[processor.mass_generator.generic]` for a tool that can predict its outputs. A new tool with its own discovery or output rules is a new processor file under `src/processor/`.