Best Practices
This page gives practical recommendations for writing clean vix.app projects.
The main idea is simple:
Keep vix.app small, explicit, and predictable.vix.app is designed to describe one clear C++ target without forcing users to write a full CMakeLists.txt.
Use one manifest per target
vix.app is intentionally simple.
Recommended:
one vix.app = one targetGood structure:
myapp/
vix.app
src/
main.cppFor tests:
myapp/
tests/
vix.app
test_app.cppFor examples:
myapp/
examples/
basic/
vix.app
src/
main.cppThis keeps each target easy to understand.
Keep the manifest readable
Prefer multi-line arrays for real projects.
Recommended:
sources = [
src/main.cpp,
src/app.cpp,
src/server.cpp,
]
include_dirs = [
include,
]Avoid very long inline arrays:
sources = [src/main.cpp, src/app.cpp, src/server.cpp, src/router.cpp, src/db.cpp]Inline arrays are fine for very small projects:
sources = [src/main.cpp]Use a stable project layout
Recommended application layout:
myapp/
vix.app
include/
myapp/
app.hpp
src/
main.cpp
app.cpp
assets/
config.jsonRecommended library layout:
mathlib/
vix.app
include/
mathlib/
math.hpp
src/
add.cpp
mul.cpp
tests/
vix.app
test_math.cppRecommended examples layout:
mathlib/
examples/
basic/
vix.app
src/
main.cppKeep main.cpp small
For applications, avoid putting all logic in main.cpp.
Prefer:
#include <myapp/app.hpp>
int main()
{
return myapp::run();
}Then put the real logic in:
src/app.cpp
include/myapp/app.hppThis makes the project easier to test.
Use include/project_name/ for public headers
Recommended:
include/
myapp/
app.hpp
config.hppThen include headers like this:
#include <myapp/app.hpp>Avoid this for larger projects:
include/
app.hppNamespaced include paths reduce collisions with other libraries.
Use src/ for implementation
Recommended:
src/
main.cpp
app.cpp
server.cppPublic headers should usually go under:
include/Private implementation headers can go under:
src/Example:
src/
detail/
parser.hppIf your source includes private headers from src/, add:
include_dirs = [
include,
src,
]Do not list headers as sources
Usually, do not do this:
sources = [
src/main.cpp,
include/myapp/app.hpp,
]Prefer:
sources = [
src/main.cpp,
]
include_dirs = [
include,
]Headers are included by the compiler through include_dirs.
Use clear target names
The name field should be stable and simple.
Recommended:
name = myappname = mathlibname = my_appAvoid:
name = "my app"Use only simple characters:
letters
numbers
_
-The target name is used by vix build and vix run.
Choose the correct target type
Use:
type = executablefor applications.
Use:
type = staticfor static libraries.
Use:
type = sharedfor shared libraries.
Recommended rule:
If it has main(), use executable.
If it is reusable code, use static or shared.Prefer explicit type
Even though executable can be the default, it is clearer to write it explicitly:
name = hello
type = executable
standard = c++20
sources = [
src/main.cpp,
]This makes the manifest easier to read.
Use standard for the C++ version
Prefer:
standard = c++20or:
standard = c++23For most projects, this is enough.
Use compile_features only when you need explicit CMake compile features:
compile_features = [
cxx_std_20,
]Keep compile options minimal
A good starting point for GCC and Clang:
compile_options = [
-Wall,
-Wextra,
]For stricter projects:
compile_options = [
-Wall,
-Wextra,
-Wpedantic,
]Avoid adding too many compiler-specific flags in a project meant to be portable.
Do not put linker flags in compile_options
Incorrect:
compile_options = [
"-Wl,--as-needed",
]Correct:
link_options = [
"-Wl,--as-needed",
]Use:
compile_optionsfor compiler flags.
Use:
link_optionsfor linker flags.
Use packages and links together
Remember the rule:
packages -> find_package(...)
links -> target_link_libraries(...)Correct:
packages = [
fmt:REQUIRED,
]
links = [
fmt::fmt,
]Incorrect:
packages = [
fmt:REQUIRED,
]packages finds the package, but it does not link the imported target automatically.
Quote package values with commas
Correct:
packages = [
"Boost:COMPONENTS=system,filesystem:REQUIRED",
]Incorrect:
packages = [
Boost:COMPONENTS=system,filesystem:REQUIRED,
]Commas can split array items, so quote package values that contain commas.
Use output_dir for apps
For executable projects, this is a good default:
output_dir = binExample:
name = myapp
type = executable
standard = c++20
output_dir = bin
sources = [
src/main.cpp,
]This gives a predictable output location:
build-ninja/bin/myappUse output_dir for libraries when needed
For libraries, this can be useful:
output_dir = libExample:
name = mathlib
type = static
standard = c++20
output_dir = lib
sources = [
src/add.cpp,
]Output can be placed under:
build-ninja/lib/Keep resources close to the app
Recommended:
myapp/
assets/
config/
public/Manifest:
resources = [
assets,
config,
]If you use:
output_dir = binresources are copied next to the executable under:
build-ninja/bin/Use custom resource destinations when needed
Example:
resources = [
"data/config.json=config/config.json",
]This copies:
data/config.jsonto:
config/config.jsonnext to the built target.
Avoid absolute paths
Prefer relative paths:
sources = [
src/main.cpp,
]
include_dirs = [
include,
]
resources = [
assets,
]Avoid:
sources = [
/home/user/project/src/main.cpp,
]Relative paths make projects easier to move, share, and build on other machines.
Quote paths with spaces
If a path contains spaces, quote it:
sources = [
"src/with space.cpp",
]resources = [
"my assets",
]Better practice: avoid spaces in source and resource paths when possible.
Keep tests separate
Recommended:
myapp/
vix.app
tests/
vix.app
test_app.cppRoot vix.app:
name = myapp
type = executable
standard = c++20
sources = [
src/main.cpp,
src/app.cpp,
]
include_dirs = [
include,
]tests/vix.app:
name = myapp_tests
type = executable
standard = c++20
sources = [
test_app.cpp,
../src/app.cpp,
]
include_dirs = [
../include,
]This avoids complex multi-target syntax.
Do not include main.cpp in tests
Incorrect:
sources = [
test_app.cpp,
../src/main.cpp,
../src/app.cpp,
]Correct:
sources = [
test_app.cpp,
../src/app.cpp,
]This avoids duplicate main() errors.
Keep examples separate
Recommended:
examples/
hello/
vix.app
src/
main.cpp
threads/
vix.app
src/
main.cppEach example can be built and run independently:
cd examples/hello
vix runUse CMakeLists.txt for complex projects
Use vix.app for simple and medium projects.
Use CMakeLists.txt when you need full build-system control.
Examples:
- multiple targets in one project
- generated source files
- custom commands
- install rules
- CTest
- FetchContent
- CPM.cmake
- custom toolchains
- package export files
- advanced platform-specific logicDo not force complex CMake logic into vix.app.
Do not edit generated CMake
For vix.app projects, Vix generates:
.vix/generated/app/CMakeLists.txtDo not edit this file manually.
Edit:
vix.appThe generated CMake file can be overwritten by Vix.
Do not commit generated files
Usually, commit:
vix.appDo not commit:
.vix/generated/app/CMakeLists.txtRecommended .gitignore:
.vix/generated/
build-dev/
build-ninja/
build-release/Start simple
A good first vix.app should look like this:
name = hello
type = executable
standard = c++20
sources = [
src/main.cpp,
]Add fields only when needed.
For example, do not add packages, links, resources, or output_dir until the project actually needs them.
Recommended full app manifest
name = myapp
type = executable
standard = c++20
output_dir = bin
sources = [
src/main.cpp,
src/app.cpp,
]
include_dirs = [
include,
]
defines = [
MYAPP_VERSION="1.0.0",
]
compile_options = [
-Wall,
-Wextra,
]
resources = [
assets,
config,
]Recommended full library manifest
name = mathlib
type = static
standard = c++20
output_dir = lib
sources = [
src/add.cpp,
src/mul.cpp,
]
include_dirs = [
include,
]
compile_options = [
-Wall,
-Wextra,
]Recommended test manifest
name = mathlib_tests
type = executable
standard = c++20
output_dir = bin
sources = [
test_math.cpp,
../src/add.cpp,
../src/mul.cpp,
]
include_dirs = [
../include,
]
compile_options = [
-Wall,
-Wextra,
]Recommended package pattern
packages = [
Threads:REQUIRED,
fmt:REQUIRED,
]
links = [
Threads::Threads,
fmt::fmt,
]Keep package discovery and linking explicit.
Recommended project templates
Simple app
hello/
vix.app
src/
main.cppApp with logic
myapp/
vix.app
include/
myapp/
app.hpp
src/
main.cpp
app.cppApp with resources
myapp/
vix.app
include/
myapp/
app.hpp
src/
main.cpp
app.cpp
assets/
config/Library with tests
mathlib/
vix.app
include/
mathlib/
math.hpp
src/
add.cpp
mul.cpp
tests/
vix.app
test_math.cppLibrary with examples
mathlib/
vix.app
include/
mathlib/
math.hpp
src/
add.cpp
mul.cpp
examples/
basic/
vix.app
src/
main.cppBuild commands
Common commands:
vix buildvix runRelease build:
vix build --preset releaseClean build:
vix build --cleanVerbose build:
vix build -vRaw CMake configure output:
vix build --cmake-verbosePass extra CMake variables:
vix build -- -DCMAKE_PREFIX_PATH=/path/to/prefixReview checklist
Before committing a vix.app, check:
1. Is name simple and stable?
2. Is type correct?
3. Is standard correct?
4. Do all sources exist?
5. Are include_dirs correct?
6. Are packages and links both present when using imported targets?
7. Are resources needed and correctly named?
8. Is output_dir useful for this target?
9. Are tests in a separate manifest?
10. Is CMakeLists.txt absent if you expect Vix to use vix.app?Summary
Best practices:
- keep one target per vix.app
- use src/ and include/
- keep main.cpp small
- use tests/vix.app for tests
- use examples/<name>/vix.app for examples
- keep packages and links separate
- use output_dir = bin for apps
- do not edit generated CMake
- use CMakeLists.txt for advanced buildsvix.app should stay simple.
CMake remains available when the project needs full control.
Next steps
Continue with: