Files
libvct-spec/spec/013-build-packaging.md
T

186 lines
9.6 KiB
Markdown

# 013 — Build & Packaging
- **Status:** Draft
- **Normative language:** `MUST`, `MUST NOT`, `SHOULD`, `SHOULD NOT`, and `MAY` are to be
interpreted as described in RFC 2119.
## 1. Purpose
This file specifies how libVCT is built, what it produces, what it exposes to embedders, and who
owns memory across the API boundary. It also fixes the configuration, threading, versioning, and
determinism contracts that the rest of the specification depends on.
## 2. Scope
Covers the toolchain, the build system, the shipped artifacts, the two API surfaces, module
visibility, arena ownership, the `Config` struct, the threading contract, versioning, and the
determinism guarantee. It does not restate stage semantics; those live in the stage files.
## 3. Definitions
| Term | Meaning |
|---|---|
| **C API** | The `extern(C)` surface exposed to non-D callers. |
| **D API** | The native D surface; the implementation itself. |
| **Shim** | The thin translation layer implementing the C API over the D API. |
| **Arena** | A region of memory reclaimed as a unit, not per object. |
| **Bump allocator** | The default in-tree allocator that serves requests by advancing a pointer. |
| **Cold path** | Code that runs at most a few times per compilation: driver, diagnostics, CLI. |
## 4. Toolchain
1. The library **MUST** be written in D.
2. Release builds **MUST** use LDC2, chosen for generated-code quality.
3. Debug builds **MUST** use DMD, chosen for compile and iteration speed.
4. libVCT **MUST** have zero runtime dependencies beyond its own code. It **MUST NOT** require a
third-party library at build time or at load time.
5. A D compiler is required to build libVCT. A system C compiler is required only when the C path
is used, and it **MUST** be discovered at runtime (see [010 — Driver & CLI](010-driver-cli.md)).
Prebuilt binaries **MUST NOT** require a D compiler at load time.
## 5. Build system
6. The build system **MUST** be xmake. A single `xmake.lua` **MUST** drive every target.
7. The build system **MUST NOT** be dub.
8. The `release` profile **MUST** select LDC2. The `debug` profile **MUST** select DMD.
9. Building libVCT **MUST NOT** require an external package manager.
## 6. Artifacts
10. A conforming build **MUST** produce all of the following:
| Artifact | Kind | Purpose |
|---|---|---|
| `libvct.a` | static library | Static linking of the full library. |
| `libvct.so` | shared library | Dynamic linking; embedders load it at runtime. |
| `vct` | executable | The command-line driver (see [010 — Driver & CLI](010-driver-cli.md)). |
| `vctc.h` | C header | The declarations of the C API. |
| `vct.test.hirbuild` | D module | The fixture harness, shipped publicly (see [012 — Testing](012-testing.md)). |
11. The static and shared libraries **MUST** expose the same symbols and behavior; choosing one
over the other **MUST NOT** change observable results.
## 7. Public API surfaces
12. The **C API** **MUST** be the primary public surface in v1. It **MUST** be declared
`extern(C)` so that C, C++, and other FFI callers can use it without a D toolchain.
13. The C API **MUST** represent library state through opaque handles for `Context`, `Module`,
`Builder`, and `Config`. Callers **MUST NOT** depend on the layout of these types, and the
header **MUST NOT** expose their internal fields.
14. The C API **MUST** provide operations to build IR, run the pipeline, and query diagnostics.
15. The **D API** **MUST** be the full-feature implementation surface. The C API is a shim over
it.
16. The shim **MUST NOT** contain logic beyond argument marshalling, error translation, and
handle conversion. Every behavior reachable through the C API **MUST** be implemented in the
D API, so that every C API call exercises the D API.
17. An implementation **MUST NOT** place optimizer, lowering, or backend logic in the header or
the shim.
An illustrative header shape (not a complete declaration set):
```c
typedef struct vct_context vct_context;
typedef struct vct_module vct_module;
typedef struct vct_builder vct_builder;
typedef struct vct_config vct_config;
vct_context *vct_context_create(const vct_config *cfg);
void vct_context_destroy(vct_context *ctx);
```
## 8. Module visibility
18. The following modules **MUST** be public: `vct.ir.hir`, `vct.traits`, `vct.context`, the
entry points of `vct.backend.c` and `vct.backend.llvm`, `vct.driver`, `vct.diag`, and
`vct.test.hirbuild`.
19. The following modules **MUST** be internal: `vct.ir.vir`, `vct.lower`, `vct.hir.opt`,
`vct.vir.opt`, and the internals of `vct.comptime`.
20. A public module **MUST NOT** expose internal types in its public signatures in a way that
forces embedders to depend on internal layout. The trait vocabulary and node identity cross
this boundary and are versioned accordingly (see section 12).
## 9. Memory ownership and lifecycle
21. A `Context` **MUST** own one or more arenas. An arena is the unit of reclamation. The default
is one arena per `Module`, freed wholesale.
22. All IR and optimizer objects **MUST** be arena-owned and non-GC. Passes **MUST** mutate them
in place under epoch guards (see [002 — Traits](002-traits.md)).
23. Garbage collection **MUST** be permitted only on cold paths: the driver, diagnostics, and the
CLI. The optimizer, lowering, and backends **MUST NOT** depend on the GC for IR objects.
24. The `Context` **MUST** accept an `ArenaAllocator` interface so embedders can supply their own
backing memory. The interface **MUST** provide exactly these operations:
- `allocate(size, alignment)` returns uninitialized storage owned by the arena; callers MUST
NOT assume zeroed storage;
- `reset()` returns the arena to its initial state so it can be reused;
- `destroy()` releases the arena's backing memory.
25. A default bump allocator **MUST** ship in-tree and **MUST** be the allocator used when the
embedder supplies none.
26. Ownership rules:
- Objects allocated into a `Context` or arena live until that arena is reset or destroyed.
- A caller **MUST NOT** free an individual arena-owned object. Freeing happens only through
`reset` or `destroy`.
- A pointer or handle returned across the API **MUST** remain valid until the owning arena is
reset or destroyed.
- Use of an object after its arena has been reset or destroyed is undefined, and the
implementation **MUST NOT** be required to detect it.
## 10. Configuration
27. A single `Config` **MUST** carry every setting that affects output. At minimum it **MUST**
carry:
| Field group | Contents |
|---|---|
| Optimization level | `-O0`, `-O1`, `-O2`, `-O3`, `-Ofast`, `-Oz`. |
| Target | `-march`, `-mcpu`, `-target <triple>`. |
| Budgets | comptime and HIR budgets (see [004 — HIR Optimizer](004-hir-optimizer.md)) and VIR budgets (see [007 — VIR Optimizer](007-vir-optimizer.md)). |
| Pass toggles | enable and disable for every pass exposed as `-f` and `-fno-`. |
| Warning policy | `-Wall`, `-Wextra`, `-Werror`, `-Wno-error[=<category>]`, `-w`. |
| C compiler selection | `-cc=gcc` or `-cc=clang`, or automatic discovery. |
| Mode | `-S`, `-emit-llvm`/`-llvm`, or `-mangled`. |
28. The CLI **MUST** be a pure function from `argv` to a `Config`. The library **MUST** take a
`Config` and **MUST NOT** read process arguments itself.
29. Programmatic callers **MUST** be able to bypass the CLI and construct a `Config` directly.
30. The determinism guarantee (section 13) is stated relative to a fixed `Config`.
## 11. Threading
31. libVCT **MUST** be thread-safe when each compilation unit has its own `Context` and arenas.
32. libVCT **MUST NOT** hold shared mutable global state.
33. Intra-module parallelism is internal and **MUST** be bounded by the `-j` setting. The
observed output **MUST NOT** depend on `-j` (see [000 — Overview](000-overview.md)).
## 12. Versioning
34. The library **MUST** follow semantic versioning.
35. The textual IR format **MUST** carry its own version, independent of the library version.
36. The trait vocabulary **MUST** carry its own version. Adding an attribute or request is a
backward-compatible change; changing the meaning of an existing one is a major change.
37. Experimental passes **MUST** be gated behind feature flags so that default behavior stays
stable within a minor version.
## 13. Determinism guarantee
38. For a fixed `Config` and a fixed input, libVCT **MUST** produce byte-identical output on every
run, independent of thread count. This binds HIR optimization, lowering, VIR optimization,
and both backends. It is a correctness property, not an optimization.
39. Determinism **MUST** be demonstrated in CI as specified in [012 — Testing](012-testing.md).
## 14. Invariants
- The C API and the D API **MUST** expose the same behavior; a divergence is a bug in the shim.
- An arena **MUST** be reclaimable as a unit; no arena-owned object may outlive its arena.
- A stage **MUST NOT** depend on shared mutable global state.
- The same `Config` and input **MUST** yield the same bytes regardless of `-j`.
## 15. Cross-references
- [000 — Overview](000-overview.md) for goals, non-goals, and the determinism guarantee.
- [001 — Architecture](001-architecture.md) for the module map and interfaces.
- [002 — Traits](002-traits.md) for epochs and the trait vocabulary version.
- [004 — HIR Optimizer](004-hir-optimizer.md) for budget names and defaults.
- [007 — VIR Optimizer](007-vir-optimizer.md) for VIR budget names and defaults.
- [010 — Driver & CLI](010-driver-cli.md) for flag semantics and C compiler discovery.
- [012 — Testing](012-testing.md) for determinism tests and the fixture harness.