vix.app Generated CMake
vix.app is the user-facing project description.
CMake is the compatibility layer underneath.
The current implementation works like this:
vix.app
-> parse manifest
-> validate manifest
-> generate .vix/generated/app/CMakeLists.txt
-> configure with CMake
-> build with CMake/NinjaThe generated CMake file is not meant to be edited by the user.
It is an implementation detail that lets Vix support a simpler project format without breaking compatibility with the C++ ecosystem.
Why generate CMake?
The main reason is compatibility.
CMake already understands:
compilers
generators
toolchains
targets
linking
find_package(...)
platform differences
Ninja integration
build directoriesInstead of replacing all of that immediately, Vix can start with a smaller layer:
vix.app -> generated CMakeThis gives the user a simple manifest while still using the existing build infrastructure underneath.
The user-facing file
The user writes:
name = hello
type = executable
standard = c++20
sources = [
src/main.cpp,
]This file is small.
It only describes the target.
It does not need to contain:
cmake_minimum_required(...)
project(...)
add_executable(...)
target_compile_features(...)
target_include_directories(...)Vix generates those details internally.
The generated file location
For vix.app projects, Vix writes the generated CMake file under:
.vix/generated/app/CMakeLists.txtExample project:
hello/
vix.app
src/
main.cppAfter running:
vix buildVix may create:
hello/
.vix/
generated/
app/
CMakeLists.txtThis generated file is part of Vix’s internal build metadata.
Do not edit generated CMake
Do not edit:
.vix/generated/app/CMakeLists.txtEdit:
vix.appThe generated file can be replaced whenever Vix regenerates the project.
If you need to change the target, sources, include directories, links, packages, or resources, change the manifest.
A minimal example
Input vix.app:
name = hello
type = executable
standard = c++20
sources = [
src/main.cpp,
]The generated CMake is conceptually similar to:
cmake_minimum_required(VERSION 3.24)
project(hello LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(hello
/absolute/path/to/project/src/main.cpp
)The exact generated output may evolve, but the idea is stable:
manifest fields become target-level CMake instructionsMapping vix.app to CMake
The generator translates manifest fields into CMake concepts.
| vix.app field | Generated CMake concept |
|---|---|
name |
target name and project name |
type = executable |
add_executable(...) |
type = static |
add_library(... STATIC ...) |
type = shared |
add_library(... SHARED ...) |
standard |
CMAKE_CXX_STANDARD |
sources |
target source list |
include_dirs |
target_include_directories(...) |
defines |
target_compile_definitions(...) |
compile_options |
target_compile_options(...) |
link_options |
target_link_options(...) |
compile_features |
target_compile_features(...) |
packages |
find_package(...) |
links |
target_link_libraries(...) |
resources |
post-build copy commands |
output_dir |
target output directory properties |
Target generation
The type field decides what target command is generated.
For an executable:
name = hello
type = executableVix generates the equivalent of:
add_executable(hello
...
)For a static library:
name = mathlib
type = staticVix generates the equivalent of:
add_library(mathlib STATIC
...
)For a shared library:
name = plugin
type = sharedVix generates the equivalent of:
add_library(plugin SHARED
...
)This keeps the manifest small while still producing a normal CMake target internally.
Source paths
In vix.app, source paths are written relative to the project directory.
Example:
sources = [
src/main.cpp,
src/app.cpp,
]The generator resolves these paths relative to the directory containing vix.app.
Internally, it can emit normalized paths in the generated CMake file.
This avoids ambiguity when CMake is configured from:
.vix/generated/app/rather than from the user project root.
Why paths need care
The generated CMake project lives in:
.vix/generated/app/But the user’s source files live in the real project directory:
src/
include/
assets/That means the generator must be careful.
It cannot assume that relative paths inside the generated CMake file are relative to the original project root.
It must resolve user paths safely.
This is one reason generated CMake is handled by Vix instead of asking the user to edit it.
Include directories
Input:
include_dirs = [
include,
third_party/asio/include,
]Generated CMake is conceptually similar to:
target_include_directories(myapp PRIVATE
/absolute/path/to/project/include
/absolute/path/to/project/third_party/asio/include
)The manifest stays relative and readable.
The generated layer can use resolved paths.
Compile definitions
Input:
defines = [
MYAPP_VERSION="1.2.3",
MYAPP_ENABLE_LOGGING=1,
]Generated CMake is conceptually similar to:
target_compile_definitions(myapp PRIVATE
MYAPP_VERSION="1.2.3"
MYAPP_ENABLE_LOGGING=1
)Definitions remain target-local.
They are not global compiler flags.
Compile options
Input:
compile_options = [
-Wall,
-Wextra,
-Wpedantic,
]Generated CMake:
target_compile_options(myapp PRIVATE
-Wall
-Wextra
-Wpedantic
)These options are emitted as raw CMake list items, not quoted strings, because compiler flags and CMake targets often need to remain raw.
Link options
Input:
link_options = [
"-Wl,--as-needed",
]Generated CMake:
target_link_options(myapp PRIVATE
-Wl,--as-needed
)Values containing commas should be quoted in vix.app so the manifest parser does not split them incorrectly.
Compile features
Input:
compile_features = [
cxx_std_23,
]Generated CMake:
target_compile_features(myapp PRIVATE
cxx_std_23
)Most projects can use:
standard = c++23But compile_features is available when explicit CMake features are needed.
Packages
Input:
packages = [
Threads:REQUIRED,
fmt:REQUIRED,
"Boost:COMPONENTS=system,filesystem:REQUIRED",
]Generated CMake is conceptually similar to:
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
find_package(fmt REQUIRED)
find_package(Boost REQUIRED COMPONENTS system filesystem)The package field only emits find_package(...).
It does not link imported targets automatically.
Links
Input:
links = [
Threads::Threads,
fmt::fmt,
Boost::system,
Boost::filesystem,
]Generated CMake:
target_link_libraries(myapp PRIVATE
Threads::Threads
fmt::fmt
Boost::system
Boost::filesystem
)This separation is deliberate.
packages discovers packages.
links links targets or libraries.
The generator does not guess which imported targets a package should link.
Why links are raw
CMake imported targets such as:
Threads::Threads
fmt::fmt
Boost::systemshould be emitted as CMake target names.
They should not be treated like ordinary quoted strings.
This is why the generator needs separate handling for:
quoted path-like values
raw CMake target-like valuesFor target links, compile features, compile options, and link options, raw output is usually the correct behavior.
Output directories
Input:
output_dir = binGenerated CMake is conceptually similar to:
set_target_properties(myapp PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)A relative output_dir is resolved under the build directory.
So with:
build-ninja/the output becomes:
build-ninja/bin/Resources
Input:
resources = [
assets,
"data/config.json=config/config.json",
]The generator emits post-build copy commands.
Conceptually:
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
/path/to/project/assets
$<TARGET_FILE_DIR:myapp>/assets
)And for a renamed file:
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
/path/to/project/data/config.json
$<TARGET_FILE_DIR:myapp>/config/config.json
)The exact generated command may differ, but the behavior is:
resources are copied next to the built target after buildWhy generated CMake is readable
The generated CMake should be readable.
Even though users should not edit it, readability matters for debugging.
A readable generated file helps engineers understand:
what Vix parsed
what Vix generated
what CMake is configuring
why a package is found or not found
why a target links or does not link
where resources are copiedGenerated code is still engineering output.
It should not be treated as a black box.
Debugging generated CMake
If a vix.app build fails, inspect:
.vix/generated/app/CMakeLists.txtThen run:
vix build -vor:
vix build --cmake-verboseYou can also pass extra CMake arguments after --:
vix build -- -DCMAKE_PREFIX_PATH=/path/to/prefixThis is useful for packages installed in custom locations.
Error boundary
The best errors should happen before CMake runs.
For example, Vix should catch:
missing name
missing sources
invalid target type
invalid standard
malformed array
unknown field
source file not found
invalid package syntaxThese errors belong at the manifest layer.
CMake should not be the first tool to discover that src/main.cpp does not exist.
CMake errors still happen
Some errors can only happen at configure or build time.
Examples:
package not found
imported target not found
compiler flag not supported
linker flag not supported
symbol not found
duplicate main()
undefined referenceThese are normal build errors.
Vix can improve diagnostics, but they still originate from the compiler, linker, or CMake.
Example: package found but not linked
Input:
packages = [
fmt:REQUIRED,
]Generated CMake finds fmt, but no link is emitted.
The build may fail if the source uses fmt.
Correct manifest:
packages = [
fmt:REQUIRED,
]
links = [
fmt::fmt,
]This produces both:
find_package(fmt REQUIRED)and:
target_link_libraries(myapp PRIVATE
fmt::fmt
)Example: wrong source path
Input:
sources = [
main.cpp,
]But the real file is:
src/main.cppVix should fail during manifest validation:
vix.app source file not found: main.cppThe fix is:
sources = [
src/main.cpp,
]This is better than generating invalid CMake and waiting for CMake to fail later.
Why not expose generated CMake as the API
One possible design would be:
vix app init
generate CMakeLists.txt
user edits generated CMakeBut that would turn the generated file into the source of truth.
That is not the goal.
The source of truth should remain:
vix.appThe generated CMake is disposable.
That allows Vix to improve the generator without forcing users to maintain generated build files.
Generated CMake and future native builds
The generated CMake layer is the current compatibility path.
It does not have to be the final build path.
Because vix.app is structured, Vix can later build directly from it:
vix.app
-> native BuildGraph
-> ObjectCache
-> Scheduler
-> LinkIn that future model, generated CMake may still exist as:
debug output
fallback path
compatibility mode
export formatBut the hot path for simple projects could become native.
Why this matters
Arbitrary CMake is hard to optimize because CMake is a full language.
Vix has to ask CMake what the build graph is.
But vix.app is declarative and narrow.
Vix can understand it directly.
That means Vix can eventually answer questions like:
Which source files belong to this target?
Which include directories affect compilation?
Which definitions changed?
Which object files can be reused?
Does this target need to link again?
Which resources need copying?This is the foundation for a faster build path.
Generated CMake as a bridge
The current design is a bridge.
It gives users a small manifest today.
It keeps CMake compatibility today.
It prepares Vix for native build execution later.
That is why generated CMake exists.
It is not the final vision.
It is the safe compatibility layer.
Common debugging checklist
When debugging a generated CMake project:
1. Check vix.app first.
2. Check .vix/generated/app/CMakeLists.txt.
3. Check that sources were resolved correctly.
4. Check include directories.
5. Check packages.
6. Check links.
7. Check output_dir.
8. Check resource copy commands.
9. Run vix build -v.
10. Use --cmake-verbose if configure output is needed.What should not be generated
The generator should not try to invent complex logic.
It should not guess:
which imported targets to link
which optional packages to use
how to fetch dependencies
how to generate sources
how to install packages
how to run CTestThose belong in explicit user configuration or full CMake.
The generator should be powerful, but not magical.
Conclusion
Generated CMake is the bridge between vix.app and the existing C++ build ecosystem.
The user writes a small manifest.
Vix validates it.
Vix generates a normal CMake project internally.
CMake and Ninja build it.
The generated file remains internal and disposable.
This gives Vix a practical path:
simple manifest today
CMake compatibility today
native BuildGraph tomorrowThat is the engineering role of generated CMake in vix.app.