Workspace & Projects
How Confµgami finds your files, how .confu files structure a workspace into CfgSpaces, the projects inside it, and how to arrange workspaces and CfgSpaces for one
product, for several processes, and for products that need different definitions.
Workspace Discovery
Confµgami operates on a workspace like an IDE. A directory tree in which it discovers definition and configuration files by their suffixes recursively.
The discovery climbs up from the folder a run starts in to the first .confu with workspaceRoot: true, which becomes the workspace root.
Without one, the nearest folder holding .git, .svn or .vscode becomes the root, and without one of those the start folder itself.
Two consequences matter in practice:
-
A configuration file that is not inside the workspace, or that discovery skips, is invisible. Validation reports no problems for a file it never read.
cfu workspace listshows what was actually found. -
The definition must be discoverable too. If
$definitionnames a definition that is not in the workspace, you get an unresolved definition error on your configuration file.
.confu - Settings Files
A .confu file holds Confµgami settings for the folder it sits in and everything below it.
The format is JSON5.
{
// Stop upward discovery. This folder is the workspace root
workspaceRoot: true,
// Load definitions that are outside the folder tree
definitions: [
"../platform/definitions"
],
}
-
workspaceRoot: truemarks the folder as the workspace root, so discovery does not climb further up the directory tree. The default isfalse. -
definitionslists definition files or directories to load, relative to the.confufolder. Use it when the definitions are delivered outside your workspace, for example in an SDK folder. Entries may use${env:NAME}or${env:NAME:default}environment variable substitution. A variable that is not set and has no default is an error. The env variable must hold a single path. -
cfgSpace: truemarks the folder as a CfgSpace root (see Workspaces, DefSpaces, and CfgSpaces). -
ignoreTraversal: truestops the downward discovery here. Files in that folder are not discovered.
cfgSpace cannot be combined with workspaceRoot or ignoreTraversal.
Combining workspaceRoot: true with ignoreTraversal: true is valid.
The folder stays invisible to enclosing workspace, but is scanned as its own workspace when passed directly as the input path.
This can be used to put isolated workspaces into workspaces.
Workspaces, DefSpaces, and CfgSpaces
A workspace holds exactly one DefSpace and any number of CfgSpaces. The DefSpace is the set of all definitions of the workspace, merged into one shared model.
A CfgSpace is a configuration namespace inside the workspace, rooted at a folder whose .confu sets cfgSpace: true.
Think of it as a project: the configuration files inside one CfgSpace form one unit that is validated, merged, and cross-referenced together, separate from every other CfgSpace.
| Scope | What it holds | What resolves only within it |
|---|---|---|
Workspace |
A root folder and everything discovery finds below it, plus the definition folders its |
The DefSpace and its CfgSpaces, the one |
DefSpace |
All definitions of the workspace. Exactly one per workspace, shared by all its CfgSpaces. |
Definition names: each exists once, in one version. |
CfgSpace |
The configuration files below its cfg root folder. Any number per workspace. |
Entity names, reference targets, mixins, and the merge itself. |
The workspace root is a CfgSpace itself, so without any CfgSpace folder the whole workspace is one CfgSpace.
A configuration file belongs to the deepest CfgSpace folder above it.
The rule of thumb: CfgSpaces separate configurations, workspaces separate definitions.
One Workspace, One CfgSpace
The default layout, for one product with one configuration:
product/
├── .confu // workspaceRoot: true
├── definitions/
│ ├── CanStack.def.json5
│ └── CanStack.default.def.json5
└── config/
├── CanStack.cfg.json5
├── Can1.cfg.json5
└── Can2.cfg.json5
No cfgSpace is needed, every configuration file in the tree belongs to the root CfgSpace.
The folders below the root carry no meaning, a file in config/ merges with a file in any other folder alike.
The .confu with workspaceRoot: true is optional, since discovery finds a root without it.
Set it anyway, so the root depends neither on where the repository begins nor on the folder you start cfu in.
Use this layout when:
-
One product or ECU has one configuration.
-
Several files only split that configuration by topic or by owner, as described in Working with Multiple Files.
Move on as soon as two files describe the same entity for two different products. They would collide or get merged within one CfgSpace.
One Workspace, Several CfgSpaces
For several processes that share the same definitions:
product/
├── .confu // workspaceRoot: true
├── definitions/
│ ├── CanStack.def.json5
│ ├── CanStack.default.def.json5
│ └── Nm.def.json5
├── process-a/
│ ├── .confu // cfgSpace: true
│ ├── CanStack.cfg.json5
│ └── Nm.cfg.json5
├── process-b/
│ ├── .confu // cfgSpace: true
│ ├── CanStack.cfg.json5
│ └── Nm.cfg.json5
└── process-c/
├── .confu // cfgSpace: true
└── CanStack.cfg.json5
Each process folder carries a .confu file:
{
cfgSpace: true,
}
The definitions are declared once and every process is validated against them. Each CfgSpace isolates its configuration files. The same entity name in two processes does not collide, a reference never resolves into another process, and every process merges on its own.
Use this layout when:
-
A product has several processes, ECUs or customer projects.
-
All of them work with the same definitions in the same versions.
-
One CI job shall check them together:
cfu cfg validate productvalidates every CfgSpace in one run.
Details that matter in practice:
-
A CfgSpace inside another one takes its files out of the outer one. A file belongs to the deepest CfgSpace folder above it.
-
A configuration file directly in the workspace root, or anywhere outside a CfgSpace folder, belongs to the root CfgSpace. Keep the root free of configuration files once you use CfgSpaces.
-
A command that works on one CfgSpace, such as
cfu cfg export-merged, takes the CfgSpace folder as its path. -
cfu workspace listshows the CfgSpace every file landed in.
Several Workspaces
CfgSpaces are not enough once the definitions themselves differ, because all CfgSpaces of a workspace share its one DefSpace. That is the case when:
-
Two products need different versions of the same definition. A definition name exists once per workspace, a second definition of that name is a duplicate definition error.
-
Two products follow different
globalSettings. -
The definitions come from separate SDK deliveries that are updated independently.
Each definition set then gets a workspace of its own, with CfgSpaces inside as before:
repo/
├── gen1/
│ ├── .confu // workspaceRoot: true
│ ├── definitions/
│ │ └── CanStack.def.json5 // definitionVersion: "1.4"
│ ├── ecu-front/
│ │ ├── .confu // cfgSpace: true
│ │ └── CanStack.cfg.json5 // $definition: "CanStack@1.4"
│ └── ecu-rear/
│ └── ...
└── gen2/
├── .confu // workspaceRoot: true
├── definitions/
│ └── CanStack.def.json5 // definitionVersion: "2.0"
├── ecu-front/
│ └── ...
└── gateway/
└── ...
A run started anywhere inside gen1/ climbs up to the first .confu with workspaceRoot: true and stops there, so it never sees gen2/.
A run started at repo/ itself has no such .confu to stop at and treats the whole repository as one workspace, reporting both CanStack definitions as duplicates.
Start every run at a workspace folder or below it:
-
cfutakes the workspace as its path, so CI runs one step per workspace. Firstcfu cfg validate repo/gen1, thencfu cfg validate repo/gen2. -
The editor builds one workspace from all folders open in a window, a multi-root window included. Open each workspace in a window of its own.
Workspace inside a Workspaces
A workspace may also sit inside another one.
Its .confu then sets ignoreTraversal: true next to workspaceRoot: true (see .confu - Settings Files), which hides it from a scan of the outer
workspace while a run started inside still scans it as a workspace of its own.
A definition folder that the inner .confu lists under definitions is still loaded by the outer scan, so keep the inner workspace’s definitions inside its own folder.
Without ignoreTraversal, the outer workspace would take the inner one in as one more CfgSpace, its definitions included.
Compared with Other Systems
The three scopes have counterparts in the build systems of programming languages.
| Confµgami | Rust (Cargo) | JavaScript (pnpm) | C++ (CMake) | JVM (Gradle) | |
|---|---|---|---|---|---|
Outer scope |
workspace |
workspace |
workspace |
build tree |
build |
Marked by |
|
|
|
the top-level |
|
Shared by all units |
DefSpace |
|
|
|
version catalog |
Isolated unit |
CfgSpace |
member crate |
package |
target |
subproject |
-
How Configuration Files Work covers the file types, the binding to a definition, and the validation flow.
-
cfu CLI Reference describes
cfu workspace listand the commands that select a CfgSpace.