Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ jobs:
run: ./bootstrap/check-build-modes
- name: the user-facing commands work
run: ./bootstrap/cli-smoke
- name: build cache preserves program behavior
run: ./bootstrap/build-cache-check
- name: packages resolve and manifests stay data
run: ./bootstrap/pkg-check
- name: allocations have not risen
Expand All @@ -161,12 +163,34 @@ jobs:
- name: plum help is a real command
run: ./bootstrap/help-check

cache_objects:
name: Linux required object cache and debug locations
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v5
- name: install supported Clang major and debug tools
run: |
sudo apt-get update
sudo apt-get install -y clang-18 binutils
mkdir -p "$RUNNER_TEMP/cache-clang"
ln -s /usr/bin/clang-18 "$RUNNER_TEMP/cache-clang/clang"
echo "$RUNNER_TEMP/cache-clang" >> "$GITHUB_PATH"
- uses: actions/download-artifact@v4
with:
name: plum-linux-compiler
- name: restore freshly built compiler
run: tar -xzf plum-linux-compiler.tar.gz
- name: supported adapter must reuse objects
run: ./bootstrap/build-cache-check --require-objects

# Preserve the existing Linux check name for branch protection. A failed
# or skipped dependency must fail this check, rather than appear green.
bootstrap:
name: build from seed and validate
if: ${{ always() }}
needs: [build, bootstrap_proofs, corpus, checks]
needs: [build, bootstrap_proofs, corpus, checks, cache_objects]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
Expand All @@ -176,6 +200,7 @@ jobs:
test "${{ needs.bootstrap_proofs.result }}" = success
test "${{ needs.corpus.result }}" = success
test "${{ needs.checks.result }}" = success
test "${{ needs.cache_objects.result }}" = success

# Does the compiler work somewhere that is not the developer's Linux
# box? Only `bootstrap/platform-smoke` runs here, deliberately -- see
Expand Down Expand Up @@ -251,6 +276,8 @@ jobs:
run: tar -xzf plum-macos-compiler.tar.gz
- name: real programs work on macOS
run: ./bootstrap/platform-smoke ./sh.real
- name: build cache preserves program behavior
run: ./bootstrap/build-cache-check ./sh.real

platforms-memory-and-debug:
name: macOS memory and debug checks
Expand Down Expand Up @@ -368,6 +395,9 @@ jobs:
- name: the compiler it built works
run: ./bootstrap/platform-smoke ./sh.real

- name: build cache preserves program behavior
run: ./bootstrap/build-cache-check ./sh.real

- name: the language server answers a real session
run: ./bootstrap/lsp-smoke ./sh.real

Expand Down Expand Up @@ -456,6 +486,8 @@ jobs:
run: tar -xzf plum-windows-compiler.tar.gz
- name: real programs work on Windows
run: ./bootstrap/platform-smoke ./sh.real
- name: build cache preserves program behavior
run: ./bootstrap/build-cache-check ./sh.real

windows-lsp:
name: Windows language server smoke
Expand Down
3 changes: 2 additions & 1 deletion MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ are. This is the operating manual.
```sh
./sh build bootstrap/self_host -o sh.real # your change, compiled in
for h in check-version help-check check-shims check-declares check-builtins cross-check \
lsp-smoke test-smoke net-smoke cli-smoke \
lsp-smoke test-smoke net-smoke cli-smoke build-cache-check \
self-test stdin-smoke tty-smoke check-docs check-doc-names check-build-modes \
highlight-check pkg-check time-harnesses \
property-check doc-check alloc-check lossless-check fmt-check \
Expand Down Expand Up @@ -52,6 +52,7 @@ advice used to describe.
| `check-doc-names` | every standard-library name the documentation mentions in prose exists, with the `use` list DERIVED from `parser.std_module_names()` rather than typed out | <1s |
| `check-build-modes` | a debug build and a release build differ in the ways they are supposed to | 17s |
| `cli-smoke` | the user-facing commands nothing else runs: `plum new` scaffolds a project that runs AND whose embedded test passes, `plum doc` on an ordinary project directory, `dump-tokens`, `dump-ast` — and which `native/*.c` sources a target selects, proved with an `#error` in the directory that must not be reached | 5s |
| `build-cache-check` | isolated IR/object reuse, content/membership/embed/dependency invalidation, header/library search changes, assembler file inputs, compiler/configuration changes, mode separation, failed edits, integrity repair, inherited streams, concurrent writers/cleaning, cache I/O fallback, bounded retention, live/abandoned staging and linked debug locations after object hits/edits; `--require-objects` makes adapter bypass a failure in pinned Linux CI | ~3m |
| `time-harnesses` | this table names every harness in the loop, and every row names a script that exists. `--measure` re-measures the `time` column instead, which takes as long as the loop does | <1s |
| `check-site-links` | a built site links only to pages it contains. Not in the loop above — it needs `build-site` to have run first, and CI runs the pair | <1s |
| `check-shims` | the embedded C shims match `native_stdlib/`, and include no non-portable header outside a platform guard | 1s |
Expand Down
8 changes: 8 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ Git dependencies name a repository, a full commit hash, and a SHA-256 content ha

## Language and compiler

- `plum run` reuses validated LLVM IR; supported Linux Clang invocations
also reuse objects and link a fresh executable. Build-cache diagnostics,
bypass controls and `plum cache list/clean build` are documented in
RUNNING.md. Parsing documentation comments now reuses decoded source
characters instead of decoding the entire source for each declaration.
Equality/release helper discovery uses emission-scoped membership maps
while preserving helper order and whole-program type checking.

- Match patterns support negative numeric literals and alternatives such as `A(x) | B(_, x)`. Alternatives must bind the same names with compatible types; guards run after an alternative matches.
- String literals support `\xNN` and `\u{...}` escapes. Unknown escapes and malformed hexadecimal or Unicode escapes are rejected with source positions.
- Empty `Array.fold` accumulators infer their element types from the callback.
Expand Down
61 changes: 58 additions & 3 deletions RUNNING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,64 @@ Compile and run it in one step:
plum run myapp
```

(The bare `plum myapp` form, with no `run`, still works too, for backward
compatibility; `plum run` is the recommended, explicit spelling,
symmetric with `plum build`.)
`run` caches validated LLVM IR. On Linux with the default Clang driver,
it also caches compiled objects when it can fingerprint the compiler and
its loaded libraries. C inputs are preprocessed again so header edits and
changes in include search paths are reflected. Every run links a fresh
temporary executable and inherits your arguments, working directory and
terminal streams. Other platforms and custom `PLUM_CC` commands reuse IR
and compile native inputs normally.

The cache is under `PLUM_CACHE/build/v1`, or the same default cache root
used for packages (`XDG_CACHE_HOME/plum`, then `~/.cache/plum`). Source
contents, discovered files, dependencies, compile-time embeds, compiler
contents and build modes determine reuse. A failed edit never runs the
previous successful program. Cache I/O failures fall back to compilation.

```sh
PLUM_NO_BUILD_CACHE=1 plum run myapp # bypass all build artifacts
PLUM_NO_OBJECT_CACHE=1 plum run myapp # reuse IR, compile native inputs
PLUM_CACHE_TRACE=1 plum run myapp # cache decisions on stderr
PLUM_PROFILE=1 plum run myapp # phase timings/counts on stderr
PLUM_BUILD_CACHE_MAX_BYTES=536870912 plum run myapp # 512 MiB budget
plum cache list build
plum cache clean build
```

Build artifacts have a default 1 GiB budget. After cached compilation,
oldest-published IR, object and shim entries are removed until the cache
fits the budget. Set `PLUM_BUILD_CACHE_MAX_BYTES` to a nonnegative byte
limit to override it; zero retains no completed entries. Invalid values
use the default. Cleanup is best effort: concurrent publishers can
briefly exceed the budget, and cache I/O errors never fail a build.
Staging directories older than 24 hours are removed only when their
recorded owner process has exited; live or unknown owners are preserved.
`plum cache clean build` removes the entire build cache immediately.
These commands leave fetched packages alone; the existing `plum cache list` and `plum cache clean` continue to manage
packages. Generated C shim sources also live in the build cache so their
paths stay stable between object builds. No executable runs from the cache.

Use a cache directory trusted by your user on this machine. Shared writable
or network cache roots are unsupported: integrity hashes detect corruption,
but do not authenticate artifacts against another writer. Cached LLVM IR
can retain earlier `@embed_file` contents until eviction or explicit cleaning.

Shared shims are verified and published as complete directories, so
concurrent builds cannot read another writer's partially extracted header.

Object validation has a cost: it hashes the native toolchain and runs C
preprocessing even on a hit. Initial cache population can be slower than
an uncached run. `PLUM_NO_OBJECT_CACHE=1` keeps IR reuse while avoiding
native-object validation/population. See the
[measured results](docs/issue-63-results.md) for the local tradeoff.

Build inputs and the toolchain must remain stable during one compilation,
as they must for an ordinary build. Observed source changes prevent cache
publication. Corrupt entries are treated as misses and repaired.

Use `plum run myapp/main.plum` to run a single file. Both file and project
forms use the build cache. The self-hosted compiler requires the `run`
subcommand; a bare `plum myapp` invocation prints usage.

Or keep the binary:

Expand Down
22 changes: 22 additions & 0 deletions TOOLING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Tooling

## Build-cache diagnostics

`bootstrap/build-cache-check --require-objects` requires certified Linux
object reuse rather than allowing adapter fallback. The pinned Clang 18 CI
job uses it and verifies linked Plum/C debug locations on cold and warm runs
before and after edits. Ordinary platform runs continue to test fallback.

`PLUM_CACHE_TRACE=1 plum run <project>` reports IR/object hits, misses and
conservative fallbacks on stderr. `PLUM_PROFILE=1` reports compiler phase
timings in microseconds and emission counts. Neither changes the program's
stdout or LLVM output. See [RUNNING.md](RUNNING.md) for cache locations,
bypass controls, supported native reuse, and manual cleaning.

`bootstrap/measure-build-cache --baseline <original-compiler> --out <report.json>`
measures uncached, empty-cache, warm and edited runs sequentially, along with
checking and LLVM emission. It compares both compilers' IR for identical
inputs and records compiler digests, platform, sample spread and profiles.
Use an idle host; this is a measurement tool, not a timing assertion in CI.
See [the issue 63 results](docs/issue-63-results.md) for the baseline and
validation evidence, and [the incremental compilation proposal](docs/incremental-compilation.md)
for the next architectural stage.

## Testing

Any top-level function whose name starts with `test_` is a test. No
Expand Down
Loading
Loading