Incremental compiles: a proposal recompiles only the files that changed (or depend on a change at compile time) and reuses the rest from its parent generation, renamed. On a Moto G 2021 compiling the default front from source takes about 33 s, and recompiling a module from its stored Erlang code still about 350 ms (the Erlang compiler's passes): 24 s of a one-file change to the 68-module front. So a module is reused by renaming its compiled binary instead; on a Mac that takes 56 ms for the whole front, against 1.9 s through the Erlang compiler.
What a generation records (deps, stored next to its binaries,
Rho.Dyn.Store.deps/2), by the names the agent wrote
("Dyn.Notes"):
- per file, the modules it defines and the modules it needs at compile
time (
Rho.Dyn.Trace), ornilwhen that's unknown; - per module, the modules its compiled code names anywhere (calls, literals, attributes), from its debug info; a reused module names what its parent named.
Which files are reused (plan/2): an unchanged file whose needs are
known, none of which is dirty. A module is dirty if its file isn't
reused (changed, deleted, or itself dirty), or if its code names a dirty
module, directly or through others: a compile-time call into it may
reach the change. Repeated until nothing changes.
Reusing (recompile/4): the parent's binary with every
Dyn.G<parent>.* atom in its atom table, literals and term
chunks (debug info, attributes, docs, ...) renamed to
Dyn.G<n>.*. Code refers to atoms and literals by index, so it
is kept byte for byte, line table included: stack traces name the
parent generation's copy of the source, which is the same text. A
module whose code or literals still hold the parent generation's name
(a string built at compile time, say), or that the rename doesn't
understand, goes the slow way: the Erlang code in its debug info,
renamed, through the Erlang compiler. If that still holds the old name
or doesn't compile, it's compiled from source. Generations built by
another runtime (a Core update) are never reused from: the Keeper
rebuilds them whole.
Summary
Functions
Empty deps: nothing known, nothing reused.
The files of deps (the parent's) to reuse, given the files whose
source is the same as the parent's (unchanged).
Generation to's version of bin, a module of generation from, for
source file file: {:ok, module, binary}, or :error when it has to
be compiled from source.
The modules of generation n (by their written names) that bin's code
names, other than itself; nil when it has no debug info (it might name
any).
Types
Functions
@spec empty() :: deps()
Empty deps: nothing known, nothing reused.
The files of deps (the parent's) to reuse, given the files whose
source is the same as the parent's (unchanged).
@spec recompile(binary(), pos_integer(), pos_integer(), String.t()) :: {:ok, module(), binary()} | :error
Generation to's version of bin, a module of generation from, for
source file file: {:ok, module, binary}, or :error when it has to
be compiled from source.
@spec refs(module(), binary(), pos_integer()) :: [String.t()] | nil
The modules of generation n (by their written names) that bin's code
names, other than itself; nil when it has no debug info (it might name
any).