add odin run -cached flag - #7148
Open
i-api wants to merge 1 commit into
Open
Conversation
Contributor
|
Even if we want something like this I think you completely overlooked that we still need checking |
Author
|
you are right |
i-api
force-pushed
the
master
branch
2 times, most recently
from
August 1, 2026 18:10
8cb7adb to
f537dc1
Compare
Author
|
pushed my static libs fix |
i-api
force-pushed
the
master
branch
7 times, most recently
from
August 5, 2026 03:11
c0d043e to
94e8c3e
Compare
Author
|
@gingerBill @Kelimion please review, thanks |
i-api
force-pushed
the
master
branch
3 times, most recently
from
August 10, 2026 04:04
d85e187 to
0a3a668
Compare
i-api
force-pushed
the
master
branch
3 times, most recently
from
August 21, 2026 06:28
6134980 to
346df61
Compare
Reuse a previously built executable when nothing that affects it has changed.
odin build [-cached] <dir | file.odin>
odin run [-cached] <dir | file.odin>
odin test [-cached] <dir | file.odin>
The caching existed as -internal-cached; this exposes it, and makes an entry
trustworthy enough to be worth exposing. A hit places the entry at the output
path, so -cached changes nothing about where a build lands or what it produces.
An entry is reused only when all of the following are unchanged:
* every source file the build reads, including #load'ed files and statically
linked foreign libraries (.a, .lib, .o, .obj and assembly). Dynamic
libraries and `system:` imports are skipped, as neither is baked in.
* the build flags. The cache directory is keyed on source paths alone and the
entry name carries only the target, so -o:none and -o:speed resolve to the
same entry; unchecked, the second build is handed the first one's executable.
* the compiler itself.
* the environment variables that can reach the output: ODIN_ROOT,
ODIN_CLANG_PATH, ODIN_ANDROID_SDK, ODIN_ANDROID_NDK,
ODIN_ANDROID_NDK_TOOLCHAIN and PATH.
The compiler is an input because a new one can turn identical sources into a
different executable while nothing in the source tree reflects it: a `make` that
touches only src/ leaves every core/*.odin mtime alone. No path is hardcoded --
this is GetModuleFileNameW, _NSGetExecutablePath and /proc/self/exe -- so a
package manager's shim still resolves to the real binary. The path feeds the
cache key, so two installations never evict each other, and the mtime feeds the
manifest, so upgrading one in place invalidates what it produced.
Locating the executable is split out of internal_odin_root_dir as
internal_odin_exe_path, so that $ODIN_ROOT no longer suppresses it. $ODIN_ROOT
says where the core collections live, not which compiler is running, and it
still decides the root directory; but it used to short-circuit the lookup
entirely, which left the compiler untracked for everyone who sets it -- distro
packaging, and Odin's own BSD CI jobs. The truncation from an executable path to
its directory was repeated in all three platform lookups and is now written
once.
Environment variables are compared against the allowlist above rather than in
full. PWD, OLDPWD, SHLVL and _ are exported by ordinary shells, so comparing the
whole environment meant a `cd` or a subshell was enough to miss the cache.
Everything omitted affects diagnostics, temporary files, or where the cache
itself lives; Windows finds its toolchain through the registry, not the
environment.
The cache moves out of .odin-cache beside the output into $ODIN_CACHE_DIR, else
%LOCALAPPDATA% on Windows and $XDG_CACHE_HOME/odin or ~/.cache/odin elsewhere,
so it is not committed by accident and is shared between build directories.
`odin clear-cache` empties it, and now does so on POSIX at all:
recursively_delete_directory was Windows-only and returned false everywhere else.
Entries are published through a pid-suffixed temporary and a rename, so racing
compilers cannot hand each other a half-written file, and a truncated executable
can never satisfy a manifest. Manifests are written after the executable they
vouch for. Copies carry the source's permissions across, as gb_file_copy creates
a new file as 0666 on every platform except macOS -- writing over an existing
output used to hide that.
On a hit, an executable that `odin run` or `odin test` is about to delete anyway
is hard-linked rather than copied. macOS validates code signatures per inode, so
a fresh copy pays a full validation on first exec, which is most of what the
cache had just saved. The link keeps the entry's inode while the executable that
runs is still the output path, so a program locating resources beside itself is
unaffected. A persistent output is copied instead: sharing an inode would let the
next build's linker truncate the cache through it. A link that cannot be made,
such as a cache on another filesystem, falls back to copying.
Manifest lines are not written with gb_string_append_fmt, which formats through a
fixed 4096 byte buffer: a longer line, such as a large $PATH, was dropped in
silence, leaving fewer entries in the file than the build produced, so the counts
never matched and the cache missed on every single run. The comparisons now also
require that the manifest and the build agree on how many entries exist, as a
manifest that was a prefix of the current arguments or environment used to pass.
The pre-semantic cache check is skipped when a foreign import is present, for the
same reason #load already skips it: both name files that only the checker
resolves, so gathering there produced a shorter list, hashed to a different cache
directory, and left one behind that was created but never written to.
Known limitation: two builds of the same sources with different flags share a
cache directory and evict one another. They are never handed each other's
executable, only rebuilt.
tests/issues covers the foreign-library rebuild, that build flags and a changed
compiler each invalidate an entry, that a build leaves no temporary files behind
and keeps the restored executable runnable, and that the cache entry survives
`odin run` deleting the executable it was hard-linked to.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
so we have the option to reuse cached binaries if it doesn't require a rebuild. makes runs after the first much less painful now
@gingerBill lmk how you feel about this, cool / not cool?