* Add "Composing Modules" tutorial: calc_aggregator core module New executable tutorial (tests/tutorial-composing-modules.test.yaml) that builds calc_aggregator, a core (universal) module depending on calc_module, showcasing every LogosModuleContext capability end-to-end via logoscore: - modulePath / instanceId / instancePersistencePath getters - durable per-instance persistence (a run counter that survives a restart), wired up in onContextReady() - typed sync dependency calls (computeReport composes five calc_module calls into one map) - typed async dependency call (fibonacciAsync with a callback) - typed event subscription (onVersionReady on calc_module's versionReady) Driven entirely from the logoscore daemon (no UI), mirroring Part 1's flow. Also: - README: list the tutorial and the calc_aggregator example module - run.sh: build the module into outputs/ via --workdir (reusing the chain's calc_module, no rebuild) and clean its calc-data/ persistence dir - ci.yml: run the new spec and verify its markdown generation - outputs/: rendered tutorial + cleaned module source The typed event subscriber requires logos-cpp-sdk PR #72 (LIDL parser: accept reserved words as identifiers in name positions) so calc_module's versionReady event sidecar round-trips; the doctest's event step stays red against published deps until that lands and the module builder bumps its SDK pin. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * rerun * Address review: warn about SDK dependency, make CI non-blocking - prerequisites + event step: explicit toolchain note that the typed event subscriber needs logos-cpp-sdk#72 (calc_module's `version` event param collides with a reserved word), with a workaround (pin a fixed logos-module-builder, or skip the event step — everything else works on the released toolchain). - ci.yml: split the Composing Modules spec out of the blocking UI-chain run into its own `continue-on-error: true` step so it doesn't block merges until #72 lands; a comment says to fold it back in once the dep ships. - regenerate outputs/tutorial-composing-modules.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Re-enable Composing Modules as a blocking CI tutorial logos-cpp-sdk#72 (LIDL parser: reserved words usable as identifiers) is merged and logos-module-builder has bumped its SDK pin, so calc_module's versionReady event sidecar now round-trips and the typed onVersionReady subscriber builds on the published toolchain. - ci.yml: fold the spec back into the blocking UI-chain run (and the published two-column report); drop the temporary continue-on-error step. - spec: remove the now-obsolete toolchain prerequisite note and the event-step heads-up. - regenerate outputs/tutorial-composing-modules.md. Verified end-to-end against the published toolchain: 77/77 doctest steps pass, including the build and the full event round-trip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9.6 KiB
logos-tutorial
Tutorial series and reference documentation for building Logos modules.
Start Here
New to Logos? Start with the developer guide -- it walks through creating, building, packaging, and running your first module:
Next Tutorials
Step-by-step tutorials that build on each other. Each creates a working module you can run.
-
Part 1: Wrapping a C Library -- build
calc_module, a core module that wraps a C library (libcalc). Covers external library configuration, CMake integration, building, inspecting withlm, testing withlogoscore, and packaging withnix-bundle-lgx. -
Part 2: Building a QML UI App -- build
calc_ui, a QML-onlyui_qmlmodule that callscalc_modulethrough thelogos.callModule()bridge. No compilation needed. Scaffold:nix flake init -t ...#ui-qml -
Part 3: Building a C++ UI Module (Process-Isolated) — build
calc_ui_cpp, aui_qmlmodule with a C++ backend that runs in a separateui-hostprocess. Define the remote interface in a.repfile; the C++ backend inherits from the generatedSimpleSource; QML accesses it via a typed replica usinglogos.module()andQtRemoteObjects.watch(). Scaffold:nix flake init -t ...#ui-qml-backend -
Composing Modules: Composing Modules with the Module Context — build
calc_aggregator, acoremodule that depends oncalc_moduleand showcases everythingLogosModuleContextoffers: themodulePath/instanceId/instancePersistencePathproperties, per-instance persistence wired up inonContextReady(), typed sync and async dependency callers (modules().calc_module), and typed event subscribers. No UI — driven entirely fromlogoscore. Needs only Part 1. -
logos-dev-boost: (⚠️ EXPERIMENTAL — NOT READY) Scaffolding Modules with logos-dev-boost — use the
logos-dev-boostCLI to auto-generate modules from C library directories. Wraps libcalc (source-only) and sqlcipher (pre-built.so), including integration tests that create encrypted databases. Covers--type module,--type full-app, and--lib-dir.
Executable Tutorials
Tutorials have YAML specs in tests/ that can be both executed (to verify they work) and used to generate the .md files. They run through the shared doctest CLI, invoked directly via its flake (nix run github:logos-co/logos-doctest -- …). See docs/spec.md for the full format reference.
# Run a tutorial end-to-end (temp dir, deleted afterwards)
nix run github:logos-co/logos-doctest -- run tests/tutorial-wrapping-c-library.test.yaml --verbose
# Keep the build results in a directory of your choice (created if missing,
# never deleted). For a chained tutorial each part lands in its own subdir.
nix run github:logos-co/logos-doctest -- run tests/tutorial-cpp-ui-app.test.yaml \
--output-dir ./outputs --continue-on-fail
# Write a two-column HTML report: rendered tutorial on the left, the commands
# actually run and their output on the right. Open the file in a browser.
nix run github:logos-co/logos-doctest -- run tests/tutorial-cpp-ui-app.test.yaml \
--report ./tutorial-report.html --continue-on-fail
# Generate the .md tutorial from the YAML spec
nix run github:logos-co/logos-doctest -- generate tests/tutorial-wrapping-c-library.test.yaml
# Pin all GitHub URLs to a specific release tag
nix run github:logos-co/logos-doctest -- run tests/tutorial-wrapping-c-library.test.yaml --release tutorial-v2
nix run github:logos-co/logos-doctest -- generate tests/tutorial-wrapping-c-library.test.yaml --release tutorial-v2
Tip: developing against a local
logos-doctestcheckout? Swapgithub:logos-co/logos-doctestforpath:../logos-doctest(or wherever your checkout lives) to run your local changes.
Where the results go
-
--output-dir DIR— run intoDIRand keep it (created if missing, never auto-deleted). This is the flag to use when you want to inspect or reuse the built modules. A tutorial withrequires:(e.g. Part 3, which pulls in Parts 1 and 2) treatsDIRas the chain root and writes each project into its own subdirectory:./outputs/ ├── logos-calc-module/ # Part 1 (built first via requires:) ├── logos-calc-ui/ # Part 2 └── logos-calc-ui-cpp/ # Part 3A standalone spec (no
requires:) is written directly intoDIR. -
--workdir DIR— run into an existing directory. Unlike--output-dir, it does not create the directory, it is deleted on exit unless you add--keep-workdir, and it runs the spec standalone (prerequisiterequires:chains are skipped). Prefer--output-dirfor chained tutorials. -
Without either flag, a temp directory is created and deleted after the run (add
--keep-workdirto preserve the temp dir).
Reviewing what ran (--report)
--report PATH writes a self-contained HTML report with two columns per step:
- left — the rendered tutorial markdown (identical to the published
.md), - right — the command(s) actually executed at that step and their output, with a pass/fail badge.
It covers every step type (file writes, shell commands, check_file, and headless ui_test runs). Pair it with --continue-on-fail so the report captures the whole run instead of stopping at the first failure. CI publishes this report for every run — see .github/workflows/ci.yml.
Watching it live in a terminal (--tui)
--tui runs the same two-column view live in your terminal instead of writing a file: the left pane shows the rendered tutorial for the current step, the right pane shows the command being run and its output, updating as the run proceeds.
# Auto-advancing: steps run one after another
nix run github:logos-co/logos-doctest -- run tests/tutorial-cpp-ui-app.test.yaml --tui
# Iterative: press the down/right arrow (or space) to execute each next step
nix run github:logos-co/logos-doctest -- run tests/tutorial-cpp-ui-app.test.yaml --tui --iterative
Press q to quit at any time. --tui needs an interactive terminal and the rich package — both are bundled in the doctest flake, so no extra install is needed when using nix.
The --release flag (or the release field in the YAML) pins all {release} placeholders in GitHub URLs to a git tag, so github:logos-co/repo{release}#output becomes github:logos-co/repo/tutorial-v2#output. Set it to "" or omit it for latest.
Example Modules
Working module source code used by the tutorials:
| Directory | Module | Type | Tutorial |
|---|---|---|---|
logos-calc-module/ |
calc_module |
core (wraps libcalc) |
Part 1 |
logos-calc-ui/ |
calc_ui |
ui_qml (QML-only) |
Part 2 |
logos-calc-ui-cpp/ |
calc_ui_cpp |
ui_qml (C++ backend + QML view) |
Part 3 |
logos-calc-aggregator-module/ |
calc_aggregator |
core (depends on calc_module) |
Composing Modules |
Regenerating the outputs
The outputs/ directory (the rendered .md tutorials linked above, plus the built module source trees) is generated from the YAML specs. To regenerate it, run:
./run.sh
This:
- Runs the full tutorial chain (Part 1 → 2 → 3) into
./outputs/, executing every step so the result is verified, not just rendered. Each part lands in its own subdirectory (outputs/logos-calc-module/,outputs/logos-calc-ui/,outputs/logos-calc-ui-cpp/). It then runs the Composing Modules tutorial intooutputs/logos-calc-aggregator-module/, reusing thecalc_modulethe chain just built (--workdir, so itsrequires:chain is not rebuilt). - Generates the
.mdtutorial for everytests/*.test.yamlspec intooutputs/(tutorial-wrapping-c-library.md,tutorial-qml-ui-app.md,tutorial-cpp-ui-app.md,tutorial-composing-modules.md). - Cleans each output project so only the source remains — it removes the per-project
.git/directories (each tutorialgit inits its project), the nix out-link symlinks (lm,logos,pm,result*), build output (modules/), compiled libraries (*.dylib,*.so), and the aggregator tutorial'scalc-data/persistence scratch dir.
Pinning to a release tag
By default run.sh resolves every {release} placeholder to the latest commit on each repo. Pass --release TAG to pin them all to a git tag, so the executed commands and the generated Markdown both reference that tag:
./run.sh --release tutorial-v3
Any further arguments are forwarded verbatim to the underlying doctest run/generate calls, so you can override a single repo's ref with --release-for:
./run.sh --release tutorial-v3 --release-for logos-basecamp=main
The TAG must exist on each referenced repo, or the nix build/nix flake init steps will fail to resolve it. Pinning expands each {release} placeholder so github:logos-co/repo{release}#output becomes github:logos-co/repo/TAG#output; omitting --release leaves them at latest.
To run against a local logos-doctest checkout instead of the published flake, export DOCTEST:
DOCTEST="nix run path:../logos-doctest --" ./run.sh --release tutorial-v3
Note: the run requires Nix with flakes and pulls/builds real dependencies (Qt, the Logos SDK), so the first run is slow. On Linux, add
--continue-on-failto theruncommand inrun.shif a known-failing prerequisite step would otherwise stop the chain early.