How vix build Works
vix build is the command that turns a Vix.cpp project into build outputs.
Those outputs can be:
executables
static libraries
shared libraries
test binaries
examplesAt a high level, the command does four things:
resolve the project
create a build plan
configure the build system
build the selected targetFor normal CMake projects, Vix keeps the CMake compatibility path.
For vix.app projects, Vix can generate an internal CMake project first, then build it through the same build pipeline.
The current model is:
CMakeLists.txt
-> project resolution
-> build planning
-> CMake configure
-> CMake build
vix.app
-> project resolution
-> generated CMake
-> build planning
-> CMake configure
-> CMake buildThe long-term model is:
vix.app
-> native BuildGraph
-> ObjectCache
-> Scheduler
-> LinkBut the current implementation keeps compatibility first.
The main job of vix build
vix build should answer these questions:
What project am I building?
Which build preset is active?
Where is the build directory?
What is the CMake source directory?
What is the default target name?
Do I need to configure?
Do I need to build?
Can I reuse previous artifacts?
Can I skip work safely?A good build command should not blindly rebuild everything.
It should avoid unnecessary work while staying correct.
Project resolution
The first step is project resolution. Vix needs to find the project root. A project root is usually a directory containing one of these files:
CMakeLists.txt
vix.appThe resolution rule is:
1. If CMakeLists.txt exists, use CMakeLists.txt.
2. Otherwise, if vix.app exists, use vix.app.This rule is important because CMake remains the full-control path.
If a CMakeLists.txt exists, Vix does not ignore it.
CMake project resolution
For a CMake project:
myapp/
CMakeLists.txt
src/
main.cppthe resolved project model is:
userProjectDir = myapp/
cmakeSourceDir = myapp/
defaultTargetName = myapp
generatedFromVixApp = falseThe configure step can use:
cmake -S myapp -B myapp/build-ninjaThe build step can use:
cmake --build myapp/build-ninja --target myappwhen no explicit target is provided.
vix.app project resolution
For a vix.app project:
myapp/
vix.app
src/
main.cppVix first parses the manifest.
Then it generates an internal CMake project under:
myapp/.vix/generated/app/The resolved model becomes:
userProjectDir = myapp/
cmakeSourceDir = myapp/.vix/generated/app/
defaultTargetName = manifest.name
generatedFromVixApp = trueThis distinction is critical.
The user project is still:
myapp/But the CMake source directory is:
myapp/.vix/generated/app/Why userProjectDir and cmakeSourceDir are separate
For normal CMake projects, the two paths are the same.
For vix.app, they are different.
That means the build plan must not treat one path as everything.
Use:
userProjectDirfor real project operations:
build directory placement
.env lookup
watch mode
snapshots
cache state
exports
user diagnosticsUse:
cmakeSourceDirfor CMake configure:
cmake -S
generated CMake diagnostics
CMakeLists.txt pathIf those two paths are confused, Vix can build the wrong target, export to the wrong directory, or snapshot generated files instead of user files.
Build planning
After project resolution, Vix creates a build plan. The build plan contains derived paths and decisions. A good plan contains values like:
userProjectDir
cmakeSourceDir
projectDir
defaultTargetName
generatedFromVixApp
preset
buildDir
configureLog
buildLog
signature
cmakeVars
launcher
fastLinkerFlag
projectFingerprintThe plan is the central object used by the build pipeline. It tells the rest of the command what to configure, where to build, and what target to build.
Presets
Vix has embedded build presets.
Common presets include:
dev
dev-ninja
releaseThey map to common build configurations.
Example:
dev:
generator: Ninja
build type: Debug
build dir: build-dev
dev-ninja:
generator: Ninja
build type: Debug
build dir: build-ninja
release:
generator: Ninja
build type: Release
build dir: build-releaseSo this:
vix buildusually builds with the default development preset.
And this:
vix build --preset releaseuses the release preset.
Build directory
The build directory is where CMake and Ninja write build files and outputs.
Examples:
build-dev/
build-ninja/
build-release/For vix.app, build directories must still be created under the user project directory.
Correct:
myapp/build-ninja/Wrong:
myapp/.vix/generated/app/build-ninja/The generated CMake directory is not the user project root.
Configure step
The configure step runs CMake.
Conceptually:
cmake -S <cmakeSourceDir> -B <buildDir> -G NinjaFor a CMake project:
cmake -S myapp -B myapp/build-ninja -G NinjaFor a vix.app project:
cmake -S myapp/.vix/generated/app -B myapp/build-ninja -G NinjaThis is why cmakeSourceDir exists.
The configure step must use the directory containing the active CMakeLists.txt.
Build step
The build step runs:
cmake --build <buildDir>If a target is known, Vix can build that specific target:
cmake --build build-ninja --target myappFor vix.app, the default target comes from:
name = myappSo the build step should not guess the target from:
.vix/generated/appThe target name should come from the manifest.
Build target selection
Target selection follows this rule:
if --build-target is provided:
use it
else if defaultTargetName exists:
use it
else:
use project directory nameThis keeps old CMake behavior while supporting vix.app.
Example:
vix build --build-target serverforces the target:
serverWithout an explicit target, a vix.app project uses:
manifest.nameConfigure signature
Vix can avoid unnecessary configure work by computing a signature. A configure signature represents the inputs that affect CMake configuration.
Examples:
preset
CMake variables
toolchain file
compiler launcher
fast linker
target triple
project fingerprint
vix.app-generated CMake contentIf the signature is unchanged, Vix can skip reconfigure. If it changes, Vix should run configure again.
Project fingerprint
The project fingerprint helps detect meaningful build configuration changes. For CMake projects, important files may include:
CMakeLists.txt
CMakePresets.json
cmake/For vix.app projects, important files include:
vix.app
.vix/generated/app/CMakeLists.txtBut the build system must still remember that the user project directory is the real project root.
The generated CMake file is derived from vix.app.
CMake arguments
Vix can pass CMake variables to the configure step.
Example:
vix build -- -DCMAKE_PREFIX_PATH=/path/to/prefixEverything after -- is forwarded to CMake configure.
This is useful for cases like:
custom package prefixes
toolchain options
feature flags
external dependency locationsCompiler launcher
Vix can use a compiler launcher when available.
Examples:
ccache
sccacheA launcher helps speed up repeated compilation by caching compiler outputs. The build plan can store the resolved launcher. Then CMake can receive something like:
CMAKE_CXX_COMPILER_LAUNCHER=ccacheThis is one layer of build acceleration.
Fast linker
Vix can also prefer a fast linker when available.
Examples:
mold
lldLinking can become a bottleneck in C++ builds. Using a faster linker reduces the cost of incremental builds where only the final link step is needed. This does not remove compilation cost, but it improves the hot path.
CMake compatibility path
The current build pipeline still uses CMake/Ninja as the execution layer. This gives Vix compatibility with:
existing CMake projects
generated CMake from vix.app
CMake packages
Ninja
toolchains
compiler launchers
fast linkersThe compatibility path is important because C++ projects can be very different. Vix should not break existing workflows.
Build graph layer
Beyond running CMake, Vix can build a deeper model of the build. The build graph layer can import:
compile_commands.json
build.ninja
dependency filesFrom those files, Vix can understand:
compile tasks
link tasks
archive tasks
copy tasks
input files
output files
dependenciesThis is the beginning of a more intelligent build engine.
compile_commands.json
compile_commands.json contains the exact compiler commands used for translation units.
A typical entry includes:
working directory
source file
compiler arguments
object outputThis is useful because Vix should not guess compile flags. It can import the real command emitted by CMake/Ninja. That makes cache decisions more accurate.
build.ninja
build.ninja contains the generated build rules.
Vix can parse it to understand build edges. Edges can be classified as:
Compile
Archive
Link
Copy
Install
Utility
UnknownThis helps Vix understand the actual build tasks that Ninja will execute.
Dependency files
Compilers can emit dependency files such as:
main.dThese files describe which headers a source file depends on.
Example:
main.o: src/main.cpp include/app.hpp include/config.hppDependency files are essential for correct incremental builds. If a header changes, the object files that depend on it must become dirty.
Build nodes
A build graph contains nodes. Nodes can represent:
source files
headers
config files
packages
object files
libraries
executablesEach node can have a state:
clean
dirty
missingThis allows Vix to reason about what changed.
Build tasks
A build graph also contains tasks.
Tasks can represent:
compile
link
archive
copy
generateA compile task turns a source file into an object file. A link task turns object files and libraries into an executable. An archive task creates a static library. A copy task copies resources or outputs.
Dirty checking
A build system should know whether a task needs to run. Inputs to dirty checking can include:
source file timestamp
source file hash
header dependency state
compiler command hash
output file existence
output file metadata
build fingerprintIf nothing relevant changed, the task can be skipped. If anything relevant changed, the task should run.
Object cache
The object cache stores reusable compiled object files. A cached object is valid only if the compile inputs match. Important cache inputs include:
source content
header dependencies
compiler identity
compiler arguments
include directories
defines
C++ standard
target triple
build typeIf those match, Vix can restore the object instead of recompiling. This can make repeated builds much faster.
Artifact cache
The artifact cache works at a larger level. It can cache build outputs such as:
libraries
executables
package artifacts
compiled dependenciesThe artifact cache is useful when a whole target or dependency was already built with the same relevant inputs.
Object cache helps with translation units. Artifact cache helps with larger build products.
No-op builds
A no-op build is when nothing changed.
The ideal behavior is:
detect unchanged state
skip configure
skip compile
skip link
finish quicklyThis is where a build tool can feel extremely fast. The goal is not to make the compiler magically faster. The goal is to avoid invoking it when there is no work.
Target-aware builds
A target-aware build should only build what is needed for the requested target. If the user runs:
vix build --build-target serverVix should not do unnecessary work for unrelated targets.
This is especially important for larger projects.
For vix.app, this is simpler because one manifest usually describes one target.
For CMake projects, Vix can rely on CMake/Ninja target information.
Scheduler
A scheduler executes build tasks in dependency order. It can run independent tasks in parallel.
A simple model is:
find ready tasks
run ready tasks up to job limit
mark outputs complete
unlock dependent tasks
repeatThis is the core of parallel builds. Ninja already does this well. Vix’s own scheduler becomes important for a future native build path.
Jobs
The -j or --jobs option controls parallelism.
Example:
vix build -j 8If no job count is provided, Vix can choose a default based on available CPU cores. Parallelism speeds up compilation when multiple source files can be built independently.
Diagnostics
vix build should not only run commands.
It should make failures readable.
A good diagnostic includes:
error title
source location
message
hint
code frame when possible
raw compiler output when neededC++ build errors are often noisy. Vix can improve the developer experience by extracting the useful part.
Configure errors
Configure errors usually come from:
invalid CMake
missing packages
bad toolchain
bad CMake variables
generated CMake problemsFor vix.app, configure errors may point to:
.vix/generated/app/CMakeLists.txtBut the root cause may still be in:
vix.appGood diagnostics should make that relationship clear.
Compile errors
Compile errors usually come from source code.
Examples:
missing header
type error
template error
invalid syntax
undefined macroVix can parse compiler output and show a cleaner message. The build system should preserve the raw log when needed.
Link errors
Link errors usually come from:
missing library
missing symbol
wrong link order
package found but target not linked
duplicate main()
undefined referenceFor vix.app, a common link mistake is:
packages = [
fmt:REQUIRED,
]without:
links = [
fmt::fmt,
]packages finds packages.
links links targets.
Build logs
Vix can write configure and build logs. Logs are important because the terminal output may be filtered or styled. A full log helps debug hard build failures. Common logs include:
configure log
build log
vix logThe UI can stay clean while logs preserve details.
Output style
vix build can present progress in a more readable way than raw build output.
A build header may include:
target
preset
launcher
linker
jobsExample:
Compiling all (dev)
* launcher: ccache | linker: mold | jobs: 8The purpose is to show the important build context without flooding the terminal.
Clean builds
A clean build removes previous build outputs and starts again.
Example:
vix build --cleanThis is useful when cache or generated files are stale.
For vix.app, cleaning should affect build outputs under the user project directory.
It should not confuse the generated CMake directory with the project root.
vix.app and build
For vix.app, the current build path is:
load vix.app
validate manifest
generate CMake
create build plan
configure CMake from cmakeSourceDir
build defaultTargetNameExample manifest:
name = hello
type = executable
standard = c++20
sources = [
src/main.cpp,
]Build:
vix buildConceptual build command:
cmake --build build-ninja --target helloNative vix.app build path
The future native path can avoid CMake for simple vix.app projects.
The architecture can become:
vix.app
-> BuildGraph
-> ObjectCache
-> Scheduler
-> LinkThis means Vix would create compile and link tasks directly from the manifest. CMake would remain available as a fallback and compatibility path.
Why native build can be faster
A native vix.app build can avoid some overhead:
CMake configure
CMake generate
Ninja file parsing
extra indirectionIt can also use Vix-specific caches more directly. But this does not mean C++ compilation becomes free. It means Vix can avoid work more aggressively when it understands the project directly.
Compatibility vs speed
The realistic architecture is:
CMakeLists.txt -> compatibility path
vix.app -> fast pathCMake projects continue to work.
vix.app projects become the place where Vix can innovate faster.
This is a better strategy than trying to replace CMake for every C++ project.
Common build flow
A typical vix build flow looks like this:
1. parse CLI options
2. resolve project
3. resolve preset
4. create build plan
5. compute configure signature
6. generate vix.app CMake if needed
7. configure if needed
8. build selected target
9. import build graph if needed
10. update cache state
11. print resultNot every step runs every time. A no-op build should skip as much as possible.
Common failure points
Common failures include:
project not found
invalid vix.app
source file not found
CMake configure failed
package not found
compiler error
linker error
executable not produced
stale build directory
invalid build targetA good build command should make each failure point clear.
Good engineering boundary
vix build should not be only a wrapper around CMake.
It should be a build orchestrator.
Today, it can orchestrate:
CMake
Ninja
presets
logs
diagnostics
cache
generated CMake
build graphsTomorrow, it can orchestrate:
native BuildGraph
ObjectCache
ArtifactCache
Scheduler
LinkThe user command remains the same:
vix buildOnly the execution strategy changes.
Conclusion
vix build is the center of the Vix.cpp build experience.
It resolves the project, creates a build plan, configures the build system, builds the target, and prepares for caching and graph-aware execution.
For CMake projects, it keeps compatibility.
For vix.app projects, it starts with generated CMake and can later become a native fast path.
The important architectural lesson is:
project resolution and build planning must be preciseEspecially for vix.app, Vix must distinguish:
userProjectDir
cmakeSourceDir
defaultTargetName
generatedFromVixAppThat separation makes the current build correct. It also prepares Vix for the next step:
vix.app -> BuildGraph -> ObjectCache -> Scheduler -> Link