fluffos/docs/lpc/preprocessor/include.md

61 lines
1.8 KiB
Markdown
Raw Permalink Normal View History

2018-12-30 16:43:04 -08:00
---
title: preprocessor / include
---
# #include
2018-12-30 16:43:04 -08:00
```c
#include "defs.h"
#include <mudlib.h>
```
1995-03-12 00:14:16 -05:00
`#include` splices the named file into the compile at the point of the
directive, exactly as if its contents had been typed there. Included
files are re-read on every recompile of the including object.
1995-03-12 00:14:16 -05:00
## Search order
* **`#include "file"`** — first resolved **relative to the directory
of the including file**, then (if not found there) through the
include path.
* **`#include <file>`** — resolved through the include path only.
The include path is the config file's `include directories` list; the
master apply `get_include_path(file)` can override it per compiled
file. Names may contain subdirectories (`<sys/net.h>`), but `..` is
not allowed. Every candidate path is subject to the master's
`valid_read` check.
Add inherit_program / include_file master applies; auto hot-reload demo (#1230) * 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>
2026-07-10 10:27:27 -04:00
The master apply `include_file(compiled, from, path)` is consulted for
every directive before resolution: it can translate the include to
another path, supply the included text itself (an array-of-strings
return), or deny the inclusion. Returning `path` unchanged keeps the
behavior described above.
## Directive line details
Text after the closing `"` or `>` is ignored, so trailing comments are
fine:
```c
#include <mudlib.h> /* mud info defines */
```
The file name may also be produced by a macro:
```c
#define CONFIG "local.h"
#include CONFIG
```
Includes nest (an included file may `#include` further files) up to a
driver-enforced depth limit; diagnostics inside an included file print
the full `In file included from ...` chain (see
[diagnostics](../diagnostics)).
## #include vs inherit
`#include` copies text into each including object — every object gets
its own compiled copy. [`inherit`](../constructs/inherit) shares one
compiled program among all inheritors. Use header files for
definitions (`#define`s, prototypes) and `inherit` for code.