# C code generation example


This tour builds a small native artifact from a message schema. The
example is intentionally compact: one schema file, one hand-written
`main.c`, generated C sources, a static library, and a CLI that proves the
generated library links and runs.

The point is to show how a native builder can expose generated files,
toolchain steps, descriptors, outputs, and the materialization plan as
views of one typed program.

## Start from the artifact

The source fixture is `c-codegen/messages.def`, a tiny key/value
schema. The builder generates:

- `include/config.h` from typed configuration defines.
- `include/generated_messages.h` and `generated/generated_messages.c`
  from the schema.
- `lib/libmessages-demo.a` from the generated object.
- `bin/messages-demo`, linked against the generated library.
- `lib/pkgconfig/messages-demo.pc` as metadata for downstream users.

## Source files

These pages are the complete source for the worked example: the builder module plus the files it consumes.

- [C codegen builder module](/metaBuilder/examples/c-codegen/source/builder.nix) - Source for the C codegen builder constructor and worked native demo program.
- [messages.def](/metaBuilder/examples/c-codegen/source/messages.def) - Schema fixture consumed by the C code generator in the native demo.
- [main.c](/metaBuilder/examples/c-codegen/source/main.c) - Small CLI source linked against the generated static library.

## Builder vocabulary

`CCodegenBuilder` ornaments `BuilderSpec` with the native-builder
fields that matter here: compiler identity, schema input, config
defines, and artifact kind. The public constructor keeps the domain
surface focused on the source inputs and leaves the internal program
to spell out the build mechanics.

```nix
cCodegen {
  name = "messages-demo";
  schema = ./c-codegen/messages.def;
  main = ./c-codegen/main.c;
}
```

## Program walkthrough

The constructor lowers the spec into a sequence of operations:
read the schema and `main.c`, declare `bash`, `gcc`, and `ar`, write
the config header, generate C sources, compile the generated source,
archive the static library, link the demo CLI, run a smoke test, emit
pkg-config metadata, and declare each output.

Because those steps are data, metaBuilder can interpret them without
rebuilding the builder by hand. Dependency analysis sees the tool-use
edges, dry-run shows the build actions, plan-view exposes the shell
plan, and materialization turns the same plan into a derivation.

## One build, many views

The section below is generated from `mb.program.introspect.run
program`. It is the internalized view of the native build: validation,
dependency graph, dry-run, plan-view, descriptors, outputs, and
materialization.

The same internalized program is interpreted several ways.

### Validation

- result: `ok`

### Dependency Graph

- nodes: `8`
- edges: `5`
- services: `0`
- node `tool:bash` (tool) package `bash-interactive`
- node `tool:gcc` (tool) package `gcc-wrapper`
- node `tool:ar` (tool) package `binutils-wrapper`
- node `operation:generate-c-sources` (operation) via `bash`
- node `operation:compile-generated-object` (operation) via `gcc`
- node `operation:archive-library` (operation) via `ar`
- node `operation:link-demo-cli` (operation) via `gcc`
- node `operation:smoke-test` (operation) via `bash`
- edge `tool:bash` -> `operation:generate-c-sources` (uses-tool)
- edge `tool:gcc` -> `operation:compile-generated-object` (uses-tool)
- edge `tool:ar` -> `operation:archive-library` (uses-tool)
- edge `tool:gcc` -> `operation:link-demo-cli` (uses-tool)
- edge `tool:bash` -> `operation:smoke-test` (uses-tool)

### Dry Run - Builder

- source `messages.def`
- source `main.c`
- tool `bash`
- tool `gcc`
- tool `ar`
- write `include/config.h`
- run `generate-c-sources` with `bash`
- run `compile-generated-object` with `gcc`
- run `archive-library` with `ar`
- run `link-demo-cli` with `gcc`
- run `smoke-test` with `bash`
- write `lib/pkgconfig/messages-demo.pc`
- descriptor `messages-demo-metadata`
- transform `cli` (elf)
- transform `static-library` (archive)
- transform `headers` (directory)
- transform `pkg-config` (text)
- materialize `messages-demo-artifact` with `runCommand`
- evidence `smoke-test`

### Dry Run - Runtime

None.

### Plan View

- write `write:include/config.h`
- run `generate-c-sources`
- run `compile-generated-object`
- run `archive-library`
- run `link-demo-cli`
- run `smoke-test`
- write `write:lib/pkgconfig/messages-demo.pc`
- `write:include/config.h`: `mkdir -p "$(dirname "$out/include/config.h")"`
- `generate-c-sources`: `bash -c 'set -eu`
- `compile-generated-object`: `gcc -I"$out"/include -c ''"$out"/generated/generated_messages.c -o ''"$out"/generated/generated_messages.o`
- `archive-library`: `ar rcs ''"$out"/lib/libmessages-demo.a ''"$out"/generated/generated_messages.o`
- `link-demo-cli`: `gcc -I"$out"/include "$pathMap_45f70a987ef70685ad13cc6283095c8504a3d4d95f23567a890bb52427d3338d" ''"$out"/lib/libmessages-demo.a -o ''"$out"/bin/messages-demo`
- `smoke-test`: `bash -c 'set -eu`
- `write:lib/pkgconfig/messages-demo.pc`: `mkdir -p "$(dirname "$out/lib/pkgconfig/messages-demo.pc")"`

### Plan Shell Excerpt

```sh
pathMap_301f316e1baa45459961d7b5c3f4a9881e17adfe67b148a8295231c6d567d78f=<source:messages.def>
pathMap_45f70a987ef70685ad13cc6283095c8504a3d4d95f23567a890bb52427d3338d=<source:main.c>
mkdir -p "$(dirname "$out/include/config.h")"
cat > "$out/include/config.h" <<'METABUILDER_HEREDOC_856398558058b5929d6076680e5c3cd22f31adde4a8117afd770ec6dc55cfd7d'
#define DEMO_FEATURE 1
#define MESSAGE_LIMIT 8

METABUILDER_HEREDOC_856398558058b5929d6076680e5c3cd22f31adde4a8117afd770ec6dc55cfd7d
bash -c 'set -eu
schema="$1"
```

### Runtime Services

None.

### Descriptors

- descriptor `messages-demo-metadata` (payload keys `artifactKind`, `compiler`, `configDefines`, `generated`, `kind`)

### Builder Self-Documentation

- documented operations: `19`
- runtime services: `0`

### Materialized Outputs

- cli -> $out/bin/messages-demo (elf)
- static-library -> $out/lib/libmessages-demo.a (archive)
- headers -> $out/include (directory)
- pkg-config -> lib/pkgconfig/messages-demo.pc (text)

### Materialization

- derivation: `messages-demo-artifact`
- outputs: `4`
- runtime artifacts: `0`

### Substrates

- dockerfile: `supported`
- shell: `supported`

