Library Template
The library template creates a reusable C++ library project. Use it when you want to build code that can be imported by other C++ projects, published to the Vix Registry, versioned, tested, and reused.
Create a library project with:
vix new mathlib --libWhat this template is for
Use the library template when you want:
- a reusable C++ library
- a header-only package
- a clean public
include/API - tests
- examples
- a stable CMake target
- registry-safe packaging
- a project that can be published with
vix publish - a project that can depend on other registry packages with
vix add
This template is different from an application. A library is not mainly something you run. A library is something other projects include and link.
Design used by this template
The library template uses a registry-safe C++ library design.
The important ideas are:
public headers live in include/
tests are separate
examples are separate
the library exposes a stable target
the project can be published to the registryA library should be easy to consume.
That means:
- clear namespace
- clear include path
- stable target name
- tests that do not depend on examples
- examples that do not pollute consumers
- package metadata in
vix.json - reproducible dependency state through
vix.lock
Quick start
Create the library:
vix new mathlib --libEnter the project:
cd mathlibBuild it:
vix buildRun tests:
vix testsRun examples if enabled by the generated project:
vix build --build-target allGenerated structure
A generated library project usually looks like this:
mathlib/
├── include/
│ └── mathlib/
│ └── mathlib.hpp
├── tests/
│ └── test_basic.cpp
├── examples/
│ ├── basic.cpp
│ └── CMakeLists.txt
├── CMakeLists.txt
├── vix.json
└── README.mdThe exact structure can evolve between Vix versions, but the strategy stays the same:
include/ -> public API
tests/ -> validation
examples/ -> usage examples
vix.json -> package metadata, tasks, dependenciesWhat each file does
| File or folder | Role |
|---|---|
include/<name>/<name>.hpp | Public library header. |
tests/ | Test programs for the library. |
examples/ | Example programs showing how to use the library. |
CMakeLists.txt | Native build and package target definition. |
vix.json | Vix metadata, tasks, registry metadata, and dependencies. |
README.md | Generated documentation for the library. |
vix.lock | Created when registry dependencies are installed. |
Public include path
A library should expose a clean include path.
For a library named mathlib, users should include:
#include <mathlib/mathlib.hpp>This is why public headers live under:
include/mathlib/Do not put public headers directly at the project root.
Do not force consumers to include files from src/.
Public namespace
The generated library uses a namespace matching the package name.
Example:
namespace mathlib
{
// public API here
}This keeps the library easy to consume:
auto nodes = mathlib::make_chain(5);For larger libraries, keep the public namespace stable. Changing the namespace later is a breaking change.
Example generated header
A simple generated header can look like this:
#pragma once
#include <cstddef>
#include <vector>
namespace mathlib
{
struct Node
{
std::size_t id{};
std::vector<std::size_t> children{};
};
inline std::vector<Node> make_chain(std::size_t n)
{
std::vector<Node> nodes;
nodes.reserve(n);
for (std::size_t i = 0; i < n; ++i)
{
nodes.push_back(Node{i, {}});
}
for (std::size_t i = 0; i + 1 < n; ++i)
{
nodes[i].children.push_back(i + 1);
}
return nodes;
}
}The generated code is intentionally simple.
Its job is to prove that:
- the include path works
- the namespace works
- tests can include the library
- examples can include the library
- the project can be built and published
Tests
The library template includes a basic test.
A simple test can look like this:
#include <vix/tests/tests.hpp>
#include <mathlib/mathlib.hpp>
int main()
{
using namespace vix::tests;
auto ®istry = TestRegistry::instance();
registry.clear();
registry.add(TestCase("mathlib basic test", [] {
auto nodes = mathlib::make_chain(5);
Assert::equal(nodes.size(), static_cast<std::size_t>(5));
}));
return TestRunner::run_all_and_exit();
}Run tests with:
vix testsUse tests to protect your public API.
Before publishing a new version, always run:
vix testsExamples
Examples show users how to use the library.
A basic example can look like this:
#include <mathlib/mathlib.hpp>
#include <iostream>
int main()
{
auto nodes = mathlib::make_chain(3);
std::cout << "nodes=" << nodes.size() << "\n";
return 0;
}Examples are useful for:
- documentation
- smoke tests
- teaching
- verifying consumer usage
Keep examples small and focused.
Stable target name
A library should expose a stable CMake target.
For a library named mathlib, the canonical target should be:
mathlib::mathlibThis is important because consumers can depend on your library without guessing the internal target name.
Example consumer usage:
target_link_libraries(app PRIVATE mathlib::mathlib)A stable target name is part of the public API of a C++ library.
Why tests and examples are separate
Tests and examples should not be forced on every consumer.
A user who installs your library usually wants the library target, not your test executables or example programs.
That is why a clean library design keeps:
library target
test targets
example targetsseparate.
This avoids target collisions and keeps registry consumption safe.
vix.json
vix.json is the Vix project and registry metadata file.
For a library, it should describe:
- package name
- version
- description
- license
- repository
- dependencies
- tasks
- registry metadata
Example shape:
{
"name": "mathlib",
"version": "0.1.0",
"type": "library",
"description": "Small reusable C++ library.",
"license": "MIT",
"repo": "https://github.com/yourname/mathlib",
"deps": [],
"tasks": {
"build": "vix build",
"test": "vix tests",
"check": "vix check --tests",
"ci": ["vix build", "vix tests"]
}
}The exact generated shape can evolve, but the role stays the same:
vix.json = package metadata + tasks + dependency declarationsRegistry overview
The Vix Registry is used to discover, install, update, and publish reusable packages.
A library can be:
searched
added
installed
locked
updated
published
unpublishedThe local registry index must be synced before package search and dependency resolution.
Sync the registry:
vix registry syncSearch packages:
vix search softadastraUse pagination:
vix search softadastra --page 2 --limit 5The registry already contains many packages.
At the time of this documentation work, your registry has around:
135 packagesSearch packages
Use vix search to find packages.
Example:
vix search softadastraExpected output shape:
Search
query : "softadastra"
page : 1
limit : 5
softadastra/core (latest: 1.7.0)
Foundational primitives for Softadastra systems.
repo: https://github.com/softadastra/core
softadastra/fs (latest: 1.11.1)
Filesystem observation and change detection layer.
repo: https://github.com/softadastra/fsGo to the next page:
vix search softadastra --page 2 --limit 5Use search before adding a package.
It helps you confirm:
- package namespace
- package name
- latest version
- description
- repository URL
Add a dependency
A library can use another library from the registry.
Example:
vix add softadastra/core@^1.7.0Then install:
vix installThe normal dependency workflow is:
vix registry sync
vix add <namespace>/<name>[@version]
vix installExamples:
vix add softadastra/core@^1.7.0
vix add softadastra/fs
vix add softadastra/json@0.3.0When you run vix add, Vix updates dependency metadata.
If the project has vix.json, the dependency is added there.
If needed, Vix can create the dependency metadata structure.
Then vix install installs from the lock state.
vix.lock
vix.lock records the installed dependency state.
It keeps installs reproducible.
The important rule is:
vix.json -> what the project requests
vix.lock -> what the project installedCommit both files when building a reusable library:
git add vix.json vix.lock
git commit -m "chore: add registry dependencies"This allows another developer to run:
vix installand get the same dependency state.
Install dependencies
Install project dependencies:
vix installvix install uses vix.lock.
If vix.lock is missing, add a package first:
vix add <namespace>/<name>[@version]Then run:
vix installInstalled dependencies are stored in the Vix project workspace and global Vix store.
List dependencies
Show installed project dependencies:
vix listThis reads the project lock file and prints dependencies.
Use it after:
vix add
vix installto verify what is installed.
Check for new versions
Check if installed dependencies are behind the registry:
vix outdatedCheck one package:
vix outdated softadastra/coreMachine-readable output:
vix outdated --jsonStrict CI mode:
vix outdated --strictImportant rule:
vix outdated checks vix.lockSo it reports the installed state, not only what is written in vix.json.
Update dependencies
Update dependencies:
vix updateUpdate and install:
vix update --installUpdate one package:
vix update softadastra/core --installUse this flow:
vix registry sync
vix outdated
vix update --install
vix testsThen commit:
git add vix.json vix.lock
git commit -m "chore: update registry dependencies"Remove a dependency
Remove a package:
vix remove softadastra/coreThen reinstall remaining dependencies:
vix installIf needed, purge local dependency files for that package:
vix remove softadastra/core --purgeUse remove when the library no longer depends on a package.
Reset project dependency state
Reset the local project state:
vix resetThis runs:
vix clean
vix installUse this when you want to clean local build/cache state and reinstall project dependencies.
It only affects the current project.
It does not remove the global Vix directory.
Store commands
The Vix store keeps cached dependency checkouts.
Show store path:
vix store pathGarbage collect unused store entries for the current project:
vix store gcUse store cleanup carefully.
If other projects depend on cached entries, check what will be removed before deleting aggressively.
Global packages
Some registry workflows can use global packages.
Install globally when supported by your package workflow:
vix add --global softadastra/coreList global packages:
vix list --globalFor normal library development, prefer project dependencies.
Global packages are useful for tools or shared developer utilities.
Publish a library
A library can be published directly to the Vix Registry.
The basic workflow is:
vix registry sync
vix publishA safer release workflow is:
vix tests
git status
git add .
git commit -m "chore: prepare release"
git tag v0.1.0
vix registry sync
vix publish 0.1.0vix publish expects a clean Git repository.
Commit your changes before publishing.
It resolves the release version, tag, commit, and package metadata.
Dry-run publish
Before publishing for real, use:
vix publish 0.1.0 --dry-runUse dry-run to check:
- package name
- version
- repository
- tag
- commit
- registry metadata
without changing the registry.
Publish notes
You can include notes when publishing:
vix publish 0.1.0 --notes "Initial public release"Use notes to explain what changed in the version.
Cleanup after publish
If your publish workflow supports cleanup:
vix publish 0.1.0 --cleanupUse cleanup when you want Vix to remove temporary registry work after publication.
Unpublish
Unpublish is a dangerous operation.
Use it only when a package entry was published incorrectly.
Example:
vix unpublish softadastra/core@1.7.0Before unpublishing, prefer publishing a fixed version when possible.
Registry consumers may already depend on the published version.
Recommended library release workflow
Use this workflow for a normal library release:
vix registry sync
vix outdated
vix tests
vix build
git status
git add .
git commit -m "chore(release): prepare v0.1.0"
git tag v0.1.0
vix publish 0.1.0 --dry-run
vix publish 0.1.0After publishing, verify:
vix registry sync
vix search mathlibThen test consumption from another project:
mkdir -p /tmp/vix-consume-test
cd /tmp/vix-consume-test
vix new app --app
cd app
vix registry sync
vix add yourname/mathlib@0.1.0
vix install
vix buildHow another project uses your library
A consumer project runs:
vix registry sync
vix add yourname/mathlib@0.1.0
vix installThen includes your header:
#include <mathlib/mathlib.hpp>And links your package target if needed by the generated build.
For a good library, the consumer should not need to know your internal folder structure.
Versioning
Use semantic versions:
0.1.0
0.2.0
1.0.0
1.1.0
2.0.0Suggested meaning:
| Version change | Use when |
|---|---|
| Patch | Bug fix, no public API break. |
| Minor | New feature, backward-compatible. |
| Major | Breaking public API change. |
Examples:
git tag v0.1.0
vix publish 0.1.0git tag v0.2.0
vix publish 0.2.0Public API discipline
A library is judged by its public API.
Keep public headers stable.
Avoid exposing unnecessary internals.
Good public API:
include/mathlib/mathlib.hpp
include/mathlib/graph.hpp
include/mathlib/version.hppAvoid making consumers include:
src/internal/...
build/...
examples/...
tests/...The public include directory is the contract.
How to grow a library
Start simple:
include/mathlib/mathlib.hppWhen the library grows, split headers:
include/mathlib/
├── mathlib.hpp
├── graph.hpp
├── node.hpp
├── algorithms.hpp
└── version.hppKeep mathlib.hpp as the main convenience include:
#pragma once
#include <mathlib/graph.hpp>
#include <mathlib/node.hpp>
#include <mathlib/algorithms.hpp>
#include <mathlib/version.hpp>Header-only vs compiled library
The generated scaffold starts as a header-only library.
That is the simplest registry-safe starting point.
Use header-only when:
- the library is small
- templates are important
- consumers should not link compiled objects
- the implementation is simple
Move to a compiled library when:
- compile time becomes too high
- implementation should be hidden
- the library has many
.cppfiles - binary boundaries matter
For compiled libraries, keep public headers in include/ and implementation in src/.
Example:
include/mathlib/mathlib.hpp
src/mathlib.cppUsing registry dependencies inside a library
A library can depend on another registry library.
Example:
vix registry sync
vix add softadastra/core@^1.7.0
vix installThen document the dependency in your README.
If the dependency affects your public API, users must also receive it through the registry install workflow.
Keep dependencies intentional.
A library with too many unnecessary dependencies becomes harder to adopt.
CI workflow
A simple CI workflow should run:
vix registry sync
vix install
vix build
vix tests
vix outdated --strictFor release branches, use:
vix publish <version> --dry-runbefore publishing.
Common mistakes
Publishing without syncing the registry
Run:
vix registry syncbefore searching, adding, updating, or publishing packages.
Publishing with uncommitted changes
vix publish expects a clean Git repository.
Check:
git statusCommit before publishing.
Forgetting to create a tag
Publish uses the release version and Git tag.
Create a tag:
git tag v0.1.0Then publish:
vix publish 0.1.0Forgetting vix.lock
If your library uses registry dependencies, commit:
vix.json
vix.lockvix.json records what you request.
vix.lock records what was installed.
Breaking the include path
Do not move public headers randomly.
For a package named mathlib, this should stay stable:
#include <mathlib/mathlib.hpp>Changing the namespace casually
Namespace is part of the public API.
Changing:
namespace mathlibto another namespace can break users.
Adding dependencies without thinking
Every dependency becomes part of your library maintenance story.
Use vix add when the dependency is useful and intentional.
What you should remember
Create a library:
vix new mathlib --lib
cd mathlib
vix build
vix testsSearch packages:
vix registry sync
vix search softadastraAdd a dependency:
vix add softadastra/core@^1.7.0
vix installCheck versions:
vix outdatedUpdate:
vix update --installPublish:
git tag v0.1.0
vix registry sync
vix publish 0.1.0The library template is the best place to learn the Vix Registry because libraries are meant to be reused, versioned, installed, and published.
Next steps
Continue with: