mirror of
https://github.com/fluffos/fluffos
synced 2026-08-12 18:26:06 -04:00
* Add inherit_program / include_file master applies; auto hot-reload demo
New compile-time master applies, consulted for every inherit statement
and #include directive:
* mixed inherit_program(string from, string path, int priv)
Called while compiling `from` for `inherit "path";` (priv nonzero for
private inherits). A string return is an alternate path for the
inherited file; an array-of-strings return is the inherited program's
source itself (compiled via load_object_from_source under the inherit
statement's name, through the existing load_object retry loop); any
other return prevents the inheritance.
* mixed include_file(string compiled, string from, string path)
Called when `from` is about to include `path` while compiling
`compiled`. A string return is the translated path (resolved absolute
from the mudlib root or relative to the includer; returning `path`
unchanged keeps the "..."-vs-<...> search semantics); an
array-of-strings return is the included text itself (pushed as an
in-memory include buffer with the usual file-identity bookkeeping);
any other return prevents the inclusion.
Both follow the valid_override/get_include_path precedent for calling
master LPC mid-compile (skipped without a VM context or master object),
and a missing apply keeps stock behavior.
The applies expose the full compile-time dependency graph, which the
testsuite uses to demonstrate mudlib auto hot-reload on file changes:
/single/hot_reload.lpc registers as the master's compile hooks, records
which source files each program's bytecode was built from (own source,
includes, inherited programs, transitively), and its call_out poller
destructs+reloads watched blueprints whose dependency closure changed
on disk - including reloading a stale parent when only the parent's
include changed.
Testsuite: single/tests/compiler/{inherit_program,include_file}.lpc pin
the apply semantics (redirect, inline source, deny, priv flag, argument
shapes on nested includes) via the scriptable /clone/compile_hook;
single/tests/applies/hot_reload.lpc demonstrates end-to-end hot reload
over a runtime-written inherit+include fixture chain. Docs added for
both applies.
Validated: full LPC suite (532 files) x2 on RelWithDebInfo, x2 on
Debug+ASan/UBSan, plus the 297 GTest unit tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* docs: add hot reload guide for the new compile-time master applies
New concepts page (concepts/general/hot_reload.md) explaining why
"file changed -> reload" needs the compile-time dependency graph, how
the inherit_program / include_file master applies expose it, and a
step-by-step mudlib implementation with examples: master delegation,
dependency recording, closure computation, change detection with
size+mtime snapshots, parent-first reload ordering, and the call_out
poller. Documents blueprint-reload semantics and caveats (clones keep
the old program, no compiles inside the applies, records only complete
for compiles observed by the daemon), pointing at the testsuite
reference implementation. Cross-linked from both apply reference pages
and the concepts indices.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* docs/testsuite: say "master copy", not "blueprint"
Align the hot-reload guide, apply reference, daemon, and test comments
with the project's terminology for the object loaded from a file (see
clonep(3): "the master copy").
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* testsuite: edge cases for the compile-time applies; docs review fixes
compile_hooks_edge.lpc pins that unusual inherit_program/include_file
return values produce clean LPC errors or the documented behavior,
never a driver crash: empty array, non-string elements, redirect to
self, empty-string redirect, inline source vs already-loaded object,
multiline inline content, the apply itself throwing mid-compile
(safe_apply falls back to default resolution), extension-spelled
redirects, and denying the auto-included global include file.
Fixtures in /clone/adv_*; /clone/adv_hook wraps the scriptable hook
with a throwing include_file.
Docs fixes from review: the apply-page examples now look the daemon up
with find_object() instead of a path call_other (which would load the
target and trigger a compile mid-compile -- the exact pattern the same
pages forbid), and the hot-reload guide's ancestors() snippet carries
the seen-mapping argument to match the reference daemon.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* hot_reload: fix multi-watch and failed-reload handling; harden tests
Three defects from the LPC review round, each now pinned by the
demonstration test:
* check_now() collected and reloaded in one pass, so the first reload's
snapshot refresh erased the change evidence for every other watched
program sharing the dependency (a common header, or a watched parent
iterating before its watched child - which then stayed bound to the
destructed old parent forever). The stale set is now collected before
any reload runs.
* A reload whose recompile throws (a syntax error mid-edit - the most
common event in a hot-reload workflow) unwound poll() before the
re-arm, killing the poller forever and leaving the watched master
copy destructed. poll() re-arms first, check_now() catches per
program, reload_count only counts successes, and closure_changed()
treats a watched-but-not-loaded program as stale so the retry
self-heals once the file compiles again.
* The three apply tests registered master compile hooks (and the
hot-reload test armed the poller and wrote /data/hot) with cleanup on
the success path only; a thrown check would leave the hook routing
every remaining compile of the randomized run. Each test now runs its
checks under catch and unhooks/tears down unconditionally, re-raising
the error afterward.
The demo test now also covers the shared-dependency pass (watch parent
and child, change the common include, both reload in one pass) and the
broken-edit recovery cycle. The hot-reload guide's snippets are updated
to match the daemon.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
* simulate: let inline inherit source resolve its own unloaded inherits
From the C++ review round: master::inherit_program returning inline
source whose text itself inherits a not-yet-loaded program failed the
whole load with "#inherit is not supported when compiling from
in-memory source" -- reachable in the feature's primary use case, since
synthesized programs routinely inherit ordinary on-disk library files.
load_object_from_source() now runs the same iterative dance as
load_object(): when the compile aborts on an unloaded parent, load that
parent (from disk, or from further master-supplied inline source, so
synthesized-inheriting-synthesized chains work), then recompile the
same source string -- which is in hand, unlike the historical
no-filename rationale for rejecting #inherit here. Mirrors
load_object()'s guards: illegal-to-inherit-self, the duplicate-name
check after the parent's arbitrary LPC ran, and an inherit-chain bound
on the retry loop as a backstop against a master that redirects to a
fresh unloaded name on every recompile.
The inherit_program test now covers both new shapes (inline inheriting
unloaded on-disk, and two levels of inline source); the apply doc drops
the already-loaded-only caveat.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK
---------
Co-authored-by: Claude <noreply@anthropic.com>
76 lines
2.6 KiB
Markdown
76 lines
2.6 KiB
Markdown
---
|
|
title: master / include_file
|
|
---
|
|
# include_file
|
|
|
|
### NAME
|
|
|
|
include_file - control, translate, or synthesize #include files at
|
|
compile time
|
|
|
|
### SYNOPSIS
|
|
|
|
mixed include_file( string compiled, string from, string path );
|
|
|
|
### DESCRIPTION
|
|
|
|
The driver calls this apply on the master object each time the
|
|
compiler processes a #include directive: the file `path` (which
|
|
might not exist) is about to be included by `from` during the
|
|
compilation of `compiled`.
|
|
|
|
**Arguments:**
|
|
- `compiled`: the main file being compiled (with its source
|
|
extension, e.g. `"/obj/sword.lpc"`)
|
|
- `from`: the file containing the #include directive -- the same as
|
|
`compiled` for a top-level include, or the including header for a
|
|
nested one
|
|
- `path`: the include name exactly as written between the `"..."`
|
|
or `<...>` delimiters
|
|
|
|
**Return Value:**
|
|
- a *string*: the translated path of the include file. It resolves
|
|
like a quoted include: absolute from the mudlib root, or relative
|
|
to the including file, with the configured include directories as
|
|
fallback. Returning `path` unchanged keeps the default behavior,
|
|
including the `"..."`-versus-`<...>` search semantics.
|
|
- an *array of strings*: the included file's contents itself, one
|
|
element per line, used in place of any on-disk file.
|
|
- *any other value* prevents inclusion of the file `path`: the
|
|
compile fails with an error.
|
|
|
|
If the master object does not define this apply (or no master is
|
|
loaded yet), inclusion behaves as usual.
|
|
|
|
The apply runs in the middle of a compile. Like valid_override(4)
|
|
and get_include_path(4) it must not trigger another compile (do not
|
|
load or clone unloaded objects from it).
|
|
|
|
### EXAMPLE
|
|
|
|
```c
|
|
mixed include_file(string compiled, string from, string path) {
|
|
// record the dependency graph for a hot-reload daemon
|
|
// (find_object never loads -- a path call_other could trigger
|
|
// a compile, which is forbidden here)
|
|
object d = find_object(HOT_RELOAD_D);
|
|
if (d) d->note_include(compiled, from, path);
|
|
|
|
// per-domain config header
|
|
if (path == "domain.h")
|
|
return "/d/" + domain_of(compiled) + "/include/domain.h";
|
|
|
|
// generated constants, no file on disk
|
|
if (path == "version.h")
|
|
return ({ "#define LIB_VERSION " + LIB_VERSION });
|
|
|
|
return path; // default behavior
|
|
}
|
|
```
|
|
|
|
### SEE ALSO
|
|
|
|
inherit_program(4), get_include_path(4), compile_object(4)
|
|
|
|
The [hot reload guide](../../concepts/general/hot_reload.md) shows how
|
|
to build mudlib auto-reloading on top of this apply.
|