Fast Target Builds
Fast target builds explain how Vix makes repeated C++ builds faster without losing correctness. Use this guide when you want to understand why commands such as:
vix build --build-target vixcan return quickly when nothing changed, while still rebuilding safely when something did change.
The problem
C++ builds can become slow even when you only want to check one target. A normal build tool often needs to:
load project metadata
load packages
scan files
load compile_commands.json
load build.ninja
read dependency files
build the graph
resolve dirty state
delegate to NinjaThat is safe, but it can be expensive when the target is already clean. The goal of Vix is not to skip work blindly.
The goal is:
fast when safe
correct by default
fallback when neededWhat fast target builds do
A fast target build lets Vix return early when it can prove that a target is already up to date.
Example:
vix build --build-target vixExpected output shape:
Checking vix (dev)
✔ Up to date in 0.3sThis is different from pretending that nothing changed.
Vix only takes the fast path when the build state proves that the target is still valid.
The important idea
The important idea is simple:
a target is up to date only when Vix can prove itA cached build state is not enough by itself. The final output must still be valid. For an executable target, this means the binary must still exist and be executable. For a library target, the expected library output must still exist. If the output is missing, Vix must rebuild or restore it from the artifact cache.
What Vix checks
For a no-op target build, Vix can return early only when important build identity fields still match.
Examples:
same build signature
same project fingerprint
same target
same preset
same build type
same compiler identity
same project inputs
last target output exists
last target output is valid
artifact state is validIf one of these changes, Vix should not trust the previous state. It should rebuild, restore, or fall back to the normal build path.
Basic usage
Build one target:
vix build --build-target vixBuild again without changing anything:
vix build --build-target vixIf the target is clean, Vix can return quickly:
Checking vix (dev)
✔ Up to date in 0.3sCompare with --fast
Vix also supports the explicit fast path:
vix build --fast --build-target vixThe important improvement is that normal target builds can now become fast too.
Before, the fastest no-op path was usually:
vix build --fast --build-target vixNow, when the state is safe, this can also be fast:
vix build --build-target vixThat matters because the default command becomes smarter.
The user should not always need to think:
Should I use --fast here?For clean targets, Vix can make the normal command fast when correctness is proven.
Safety rule
The safety rule is:
state hit is not enough
the target output must still be validFor example:
vix build --build-target vix
rm -f build-ninja/vix
vix build --fast --build-target vixVix must not say that the target is up to date if the binary is missing.
It must rebuild or restore the target.
Artifact cache relationship
Fast target builds are part of a layered build model. The local build state is the fastest no-op validation layer. The artifact cache is the layer that can restore complete outputs. The object cache is the layer that can reuse compiled objects. The fallback is CMake and Ninja.
The model looks like this:
BuildState -> fastest no-op validation
ArtifactCache -> restore complete target
BuildGraph -> target-aware analysis
ObjectCache -> restore compile outputs
CMake/Ninja -> compatibility fallbackEach layer has a different role.
BuildState
BuildState answers:
Is this target already valid in this build directory?If the answer is yes, Vix can return quickly. This is the fastest path because there is no need to walk the full graph.
ArtifactCache
ArtifactCache answers:
Do we already have this final target output in the cache?For example, after deleting the build directory:
vix build --build-target vix
rm -rf build-ninja
vix build --build-target vixVix may restore the target:
Restoring vix (dev)
✔ Artifact cache hit
✔ DoneThat avoids recompiling when the full target artifact is already known.
BuildGraph
BuildGraph gives Vix target awareness.
It helps Vix understand:
which compile tasks belong to the requested target
which object files affect the target
which dependencies affect the target
which link step produces the targetWhen the project changes, this matters more than the no-op path. A clean no-op build may finish before the graph executor is needed. But when files change, the graph executor helps Vix rebuild only the necessary target work.
ObjectCache
ObjectCache works at the source-file level.
It can restore compile outputs such as:
BuildCommand.cpp -> BuildCommand.cpp.o
BuildCommand.cpp -> BuildCommand.cpp.dIf the final artifact cannot be reused, object cache can still reduce compilation work.
CMake and Ninja fallback
Vix still keeps CMake and Ninja as the compatibility fallback. If Vix cannot prove that a target is clean, it should delegate safely. The fallback is not a failure.
It is part of the correctness model.
if unsure, delegateExample benchmark
On one Vix.cpp benchmark setup, the result looked like this:
vix build --fast --build-target vix 313 ms
vix build --build-target vix 315 ms
VIX_GRAPH_EXECUTOR=0 vix build --build-target vix 309 msThe important conclusion is not only that --fast is quick.
The important conclusion is:
normal no-op target builds are fast by defaultThat is better for daily development.
Why Graph Executor still matters
When the benchmark shows similar no-op times with and without the graph executor, it does not mean the graph executor is useless. It means the clean no-op path is being solved earlier. For unchanged targets, the fastest layer wins. For changed projects, the graph executor still matters because it gives Vix target intelligence.
It can help with:
target resolution
target closure
dirty compile task detection
safe partial rebuilds
safe delegationExplain mode
Use --explain when you want to know why Vix rebuilds something:
vix build --explain --build-target vixExample output for a source change:
Rebuilding BuildCommand.cpp
reason: source file changed
Relinking vix
reason: object file changedFor some header changes, Vix may not yet be able to map the header precisely to selected compile tasks. In that case, it should avoid lying.
A safe explanation can look like this:
Project input changed
reason: dependency changed, delegating target to Ninja
Relinking vix
reason: target may depend on changed inputThat is the right behavior.
If Vix cannot prove that the target is clean, it must not say that it is clean.
When Vix should return early
Vix can return early when:
the build state matches
the requested target matches
the preset matches
the build type matches
the compiler identity matches
the project fingerprint matches
the target output still exists
the target output is validThis is the ideal no-op path.
When Vix should rebuild
Vix should rebuild when:
a source file changed
a header file changed
CMakeLists.txt changed
vix.app changed
vix.json changed
vix.lock changed
compiler flags changed
the requested target changed
the build preset changed
the compiler changed
the target output is missingA rebuild can still be optimized by the build graph, object cache, and artifact cache.
When Vix should fall back
Vix should fall back when it cannot prove safety.
Examples:
unknown generated input
ambiguous header dependency
unsupported build graph shape
missing dependency metadata
incomplete state file
invalid cache entryThe fallback should be conservative. A false cache miss is acceptable. A false cache hit is dangerous.
Recommended workflow
Use normal target builds first:
vix build --build-target vixUse --explain when investigating rebuild behavior:
vix build --explain --build-target vixUse --fast when you explicitly want the fastest state-based check:
vix build --fast --build-target vixUse a clean rebuild only when needed:
vix build --clean --build-target vixCommon workflows
# Build one target
vix build --build-target vix
# Repeat the same target build
vix build --build-target vix
# Explain why Vix rebuilds
vix build --explain --build-target vix
# Use the explicit fast path
vix build --fast --build-target vix
# Force a clean build
vix build --clean --build-target vixCommon mistakes
Thinking fast means unsafe
Fast target builds are only fast when Vix can prove that the target is valid.
The rule is:
fast only after validationTrusting state without checking outputs
A build state hit is not enough.
The final output must still exist and be valid.
Expecting every changed build to finish in 300 ms
The 300 ms path is for clean no-op target builds. If source, headers, flags, manifests, or build configuration changed, Vix must do more work.
Disabling fallback
Fallback is part of correctness. If Vix is unsure, delegating to Ninja is the safe behavior.
Related commands
| Command | Purpose |
|---|---|
vix build | Build a project or target |
vix build --explain | Explain rebuild decisions |
vix build --fast | Use the explicit fast validation path |
vix clean | Remove local generated state |
vix reset | Clean and reinstall dependencies |
vix info | Inspect cache and store paths |
What you should remember
Fast target builds are not about skipping work blindly. They are about proving that work is unnecessary.
The principle is:
fast when safe
correct by default
fallback when neededFor developers, this makes the ordinary command smarter:
vix build --build-target vixIf nothing changed, it can return quickly. If something changed, it rebuilds. If Vix is unsure, it falls back.