Skip to main content

Workspace

The workspace is the top-level repository that orchestrates all Ideal components. It checks out component repositories side by side under components/, builds them in dependency order, and installs their artifacts into a shared staging directory so they can discover each other at build time.

The design is described in ADR-0004 — Workspace and Component Protocol. This page is the reference for the workspace itself: its configuration, manifest format, available targets, and VCS integration.

Repository layout

ideal/
├── GNUmakefile # Workspace orchestration
├── manifest.mk # Component registry
├── local.mk # User-specific overrides (not tracked)
├── scripts/
│ └── component-vcs.sh # VCS operations for components
├── components/
│ ├── cmake-modules/ # Shared CMake build infrastructure
│ └── ... # Other components
├── docs/ # Documentation portal (Docusaurus)
├── stage/ # Staging directory (installed artifacts)
└── local/ # Local reference documents (not published)

Configuration

Defaults are defined in GNUmakefile. Override them in local.mk at the workspace root — this file is not tracked by version control.

Workspace settings

VariableDefaultDescription
VCSgitVersion control system (git/jj)
BRANCHmainDefault branch for all components
COMPONENTS_DIRcomponentsComponent checkout directory
STAGE_DIRstageStaging directory for build output
PREFIX<workspace>/stageInstall prefix
DEFAULT_REMOTEhttps://git.sr.ht/~cpradogBase URL for repositories
RELEASE_BRANCH_PREFIXstable/Release branch prefix

local.mk

When a component is cloned (make sync), the workspace generates a local.mk inside the component that includes the workspace's local.mk:

-include /path/to/workspace/local.mk
BRANCH ?= main

This gives every component access to the workspace-wide configuration. Variables set in the workspace local.mk propagate to all components automatically.

A typical workspace local.mk:

WORKSPACE_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))

VCS := jj
DEFAULT_REMOTE := git@git.sr.ht:~cpradog
PREFIX := $(abspath $(WORKSPACE_DIR)/stage)
SYSROOT := $(abspath $(WORKSPACE_DIR)/stage)
PRESET := dev

WORKSPACE_DIR resolves to the directory containing local.mk, so the paths work both when included from GNUmakefile and when included from a component's own local.mk.

Component manifest

Components are declared in manifest.mk. Each entry adds a name to the COMPONENTS list and optionally overrides its default variables.

COMPONENTS += mylib
mylib_REMOTE ?= $(DEFAULT_REMOTE)/ideal-mylib
mylib_BRANCH ?= $(BRANCH)
mylib_DEPS ?= sx

Per-component variables

VariableDefaultDescription
<name>_REMOTE$(DEFAULT_REMOTE)/<name>Repository URL
<name>_BRANCH$(BRANCH)Branch override
<name>_REV(unset)Pin to a commit or tag (takes priority over branch)
<name>_PATH$(COMPONENTS_DIR)/<name>Local checkout path
<name>_DEPS(empty)Space-separated list of component dependencies

When _REV is set, it takes priority over _BRANCH. This is useful for pinning a component to a specific release tag or commit.

Dependencies listed in _DEPS must refer to other components declared in COMPONENTS. The workspace validates this at parse time and reports an error if a dependency is not found.

Intrinsic dependencies

Components listed in INTRINSIC_DEPS are automatically added as build and install dependencies for every component in COMPONENTS. This is intended for shared build infrastructure that all components need but should not have to declare individually.

INTRINSIC_DEPS += cmake-modules

In the example above, every component automatically depends on cmake-modules at build and install time — there is no need to add it to each component's _DEPS.

Intrinsic dependencies themselves are excluded from this automatic injection to avoid circular references. If an intrinsic dependency needs another intrinsic dependency, it must declare it explicitly in its own _DEPS.

A component listed in INTRINSIC_DEPS must also be listed in COMPONENTSINTRINSIC_DEPS controls the automatic injection behavior, while COMPONENTS is where the component is actually declared and configured.

Staging directory

The staging directory (stage/ by default) is the mechanism through which components discover each other during development. When a component is built, it is immediately installed into the staging directory. Subsequent components find headers, libraries, and pkg-config files there.

The workspace passes two variables on every make invocation:

VariableDescription
PREFIXWhere the component must install its files
SYSROOTWhere dependencies have been staged (for lookup)

During workspace builds, both point to the staging directory. During make install, PREFIX points to the final installation path while SYSROOT still points to the staging directory.

Targets

Build

TargetDescription
buildBuild all components in dependency order
build-<name>Build a single component (and its dependencies)
testRun tests for all components
test-<name>Run tests for a single component

build compiles each component and installs it to the staging directory so that dependent components can find it. The order is determined by _DEPS and INTRINSIC_DEPS.

test first builds the component (if not already built), then runs its test suite. Components without a test target are silently skipped.

Install

TargetDescription
installInstall all components to PREFIX
install-<name>Install a single component
uninstallUninstall all components from PREFIX
uninstall-<name>Uninstall a single component

install rebuilds each component with the final PREFIX and installs it. This is distinct from the staging install that happens during build.

Clean

TargetDescription
cleanClean build artifacts for all components
clean-<name>Clean a single component
distcleanClean all components and remove the staging directory
distclean-<name>Distclean a single component

Documentation

TargetDescription
docsBuild component docs and the documentation site
docs-<name>Build and deploy docs for a single component
docs-serveBuild component docs and serve the site with live-reload

The docs target first builds documentation for each component (running their docs and push-docs targets), then builds the Docusaurus documentation site. Components without a docs target are silently skipped.

Version control

TargetDescription
syncClone or fetch + switch all components
sync-<name>Sync a single component
fetchFetch remote changes for all components
fetch-<name>Fetch a single component
switchSwitch all working copies to their manifest revision
switch-<name>Switch a single component
statusShow each component's status relative to the manifest
status-<name>Show status for a single component

The workspace supports both Git and Jujutsu (jj). Set VCS in local.mk to choose. The sync target clones a component if it does not exist locally, or fetches and switches if it does.

Release

TargetDescription
check-release MAJOR=XValidate that all components are ready for release
release MAJOR=XCreate release branches and tag vX.0.0

Release creates a stable/X branch in each component and tags the initial release. The documentation site is versioned as part of the release process.

See ADR-0007 — Versioning and Release Model for the full versioning policy.

Scaffolding

TargetDescription
new NAME=<name> DESC="<description>"Create a new component

Scaffolding initializes a new component repository under components/, populates it with standard project files from the cmake-modules template, adds it to manifest.mk, and configures its local.mk. Pass REPO_NAME= to override the default repository name (ideal-<NAME>).

See the Project Scaffolding reference for details on generated files and presets.

Dependency resolution

The workspace resolves build order from declared dependencies. When you run make build, the dependency chain determines the order:

  1. Components in INTRINSIC_DEPS are built first (they have no automatic dependencies on each other).
  2. Each component's explicit _DEPS are built and staged before the component itself.
  3. The combination of intrinsic and explicit dependencies forms a DAG that Make resolves automatically.

For example, given:

INTRINSIC_DEPS += cmake-modules

COMPONENTS += cmake-modules
COMPONENTS += sx
COMPONENTS += toolkit
toolkit_DEPS = sx

Running make build-toolkit triggers: build-cmake-modules (intrinsic) and build-sx (explicit dep, which itself depends on build-cmake-modules), then build-toolkit.