Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tcxTemplate

A starter template for building a TrussC addon. Click "Use this template" on GitHub (or copy this folder), rename tcxTemplate → your addon name, and start writing code.

An addon is a reusable library that drops into a TrussC project. Users add it with one line in addons.make (or trusscli addon add tcxYourAddon).


1. Quick start

# 1. Create your repo from this template, then clone it into a TrussC install:
trusscli addon clone tcxYourAddon          # (after it's published — see §6)
# ...or during development, just put the folder at <TrussC>/addons/tcxYourAddon

# 2. Build & run the bundled example:
cd addons/tcxYourAddon/example-basic
trusscli update            # generates CMakePresets.json / IDE files
trusscli run               # build + launch

Then rename things:

In this template Rename to
repo / folder name tcxTemplate tcxYourAddon
src/tcxTemplate.{h,cpp} src/tcxYourAddon.{h,cpp}
sub-namespace tcx::myaddon tcx::youraddon (addon name minus tcx, all lowercase — tcxDepthCameratcx::depthcamera)
class tcx::myaddon::Template tcx::youraddon::YourClass
addon.json fields your metadata

2. Folder layout

tcxYourAddon/
├── addon.json              # registry metadata (see §4)
├── README.md
├── LICENSES.md             # this addon's license + any bundled deps/assets
├── src/                    # ← your addon code (.h / .cpp / .mm / .m)
│   ├── tcxYourAddon.h
│   └── tcxYourAddon.cpp
├── example-basic/          # a runnable example app — a normal TrussC project
│   ├── addons.make         # lists this addon
│   ├── .gitignore          # standard project .gitignore (copy-out friendly)
│   ├── src/                # main.cpp + tcApp.{h,cpp}
│   └── (CMakeLists.txt / CMakePresets.json — generated by trusscli, gitignored)
├── libs/                   # (optional) bundled 3rd-party libs, auto-collected
└── CMakeLists.txt          # (optional) only for custom builds — see §5

Naming conventions (match the rest of TrussC):

  • Repo / folder / library target: tcxPascalCase (e.g. tcxOsc).
  • Public namespace: tcx::<addonname> — each addon gets its own sub-namespace, so two addons can both define a World / Client without clashing. Users opt in with using namespace tcx::<addonname>;. (Framework core lives in tc.)
    • The sub-namespace is the addon name minus the tcx prefix, all lowercase. No abbreviation, no camelCase: tcxOsctcx::osc, tcxBox2dtcx::box2d, tcxDepthCameratcx::depthcamera (NOT tcx::depthCamera or tcx::dc). All-lowercase keeps the spelling mechanically derivable (tooling can check it) and lets case mark the part of speech, as everywhere else in TrussC: lowercase:: = namespace, PascalCase = type, camelCase = function.
    • Keep names short inside the sub-namespace — the namespace already says whose Client it is, so prefer tcx::mqtt::Client over tcx::mqtt::MQTTClient (existing addons keep their historical names; apply this to new code).
    • Internal helpers that must live in a header go in a nested detail namespace; helpers in a .cpp go in an anonymous namespace.
    • Migrating an addon from flat tcx:: / legacy trussc::? Keep silent compat aliases until v1.0.0 (no [[deprecated]] attribute — it would warn on idiomatic unqualified use under using namespace):
      namespace tcx    { using osc::OscMessage; } // deprecated: remove at v1.0.0
      namespace trussc { using tcx::osc::OscMessage; } // deprecated: remove at v1.0.0
      See tcxOsc for the full pattern.
  • No using namespace in headers. A using-directive in a public header (at file scope or inside your namespace) silently injects names into every consumer — and into your own namespace's lookup — so qualify explicitly: tc::Vec2, std::string. File-scope using namespace std; using namespace tc; in a .cpp is fine (it's TU-local, and it's the normal TrussC app style).
  • Files: tcxXxx.h — the tcx prefix is required because every loaded addon's src/ shares one include path; a generic name like Types.h or Client.h will eventually collide with another addon's header. The prefix is your filename's namespace. Types PascalCase, functions camelCase.
  • Colors are 0.0–1.0 floats, angles are radians, use TAU (= 2π) not PI.
  • Setters return *this for chaining; log with tc::logNotice/logWarning/logError, not cout (stdout is reserved for the MCP channel).
  • MCP tools an addon registers are named tcx_<addon>_* (e.g. tcxImGui registers tcx_imgui_click). tc_* is reserved for core-provided tools, and unprefixed names belong to the app author — never squat there.

3. How a project consumes your addon

A user lists it in their project's addons.make:

tcxYourAddon

…and TrussC's build does the rest. By default there is no CMakeLists.txt in an addon — TrussC auto-collects everything:

  • compiles src/** and libs/*/src/** (.cpp .c .mm .m)
  • adds src/, include/, and libs/*/include to the include path
  • links your addon against TrussC
  • header-only addon (no sources)? It becomes an INTERFACE target automatically.

So for a plain C++ addon you write zero build config. Just put headers and sources in src/.


4. addon.json

Metadata for the registry crawler, the website, and trusscli. Must be valid JSON — no comments (the crawler pipes it through jq). An empty {} already makes the repo a discoverable addon.

To be useful, fill these five — that's the whole "minimum":

{
  "description": "One-line summary of what your addon does",
  "author": "Your Name <you@example.com>",
  "license": "MIT",
  "category": "utilities",
  "keywords": ["example", "template"]
}
Field What it does
description One-line summary in listings / trusscli addon list.
author "Name" or "Name <email>".
license SPDX id ("MIT", "Apache-2.0"…). Missing → shows "Unknown".
category One value from the fixed set (below). Unknown/omitted → "misc".
keywords Free tags for search/filtering within a category.

Optional fields

Everything else is optional — add only what's relevant, roughly in this order of how often it's actually used:

Field What it does
dependencies Other addons yours needs (below). Comes up fairly often.
screenshot Relative path ("docs/preview.png") or absolute URL. Shown on the site.
demo_url Link to a live demo (e.g. a TrussSketch page).
version Self-reported version. If omitted, the crawler uses your latest git tag — preferred, so just tag releases.
trussc_version TrussC version range you target, e.g. ">=0.5.0". Informational; least-used field.
platforms Platforms your addon targets, e.g. ["macos","windows","linux"]. Also drives CI — the reusable workflow builds exactly these. Omit → desktop default (macos/windows/linux). web/android/ios are recognized but not built by CI yet (warning, not a failure). win/osx are accepted as aliases.
name Informational only; the registry always uses the repo name. Leave it out.

category — pick exactly one

3d  ai  algorithms  animation  bridges  computer-vision  game  graphics
gui  hardware  machine-learning  network  physics  sound  typography
utilities  video  web

Anything unknown/empty maps to misc. Don't write "misc" yourself. Use keywords for finer sub-classification (e.g. category: "graphics" + keywords: ["shader", "post-fx"]).

dependencies — other addons yours needs

Plain string (latest from registry) or an object to pin a version:

"dependencies": [
  "tcxCurl",                                       // latest, from registry
  { "name": "tcxBox2d", "tag":    "v0.2.0" },      // pinned tag
  { "name": "tcxOsc",   "branch": "main" },        // pinned branch
  { "name": "tcxBaz",   "commit": "a1b2c3d" },     // pinned commit
  { "name": "tcxBar",   "url": "https://github.com/user/tcxBar" } // off-registry
]

tag/branch/commit are mutually exclusive (commit wins if you set more than one). trusscli addon clone resolves the graph recursively and aborts on cycles. (This is for addon→addon deps. For a third-party C/C++ library, use FetchContent in a custom CMakeLists.txt instead — see §5.)


5. When you need a custom build (CMakeLists.txt)

Add a CMakeLists.txt at the addon root only for things auto-collection can't do: FetchContent, custom flags, code generation, platform link libs. When present, TrussC add_subdirectory()s it and you own the target fully.

This template includes a fully-commented CMakeLists.txt.example — rename it to CMakeLists.txt and edit. The essentials:

set(ADDON_NAME tcxYourAddon)               # must equal the folder name
file(GLOB ADDON_SOURCES src/*.cpp)
add_library(${ADDON_NAME} STATIC ${ADDON_SOURCES})
target_include_directories(${ADDON_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(${ADDON_NAME} PUBLIC TrussC)   # always link TrussC

Real examples in the framework: tcxBox2d (FetchContent), tcxTls (FetchContent + configure_file codegen + per-platform libs).

Bundling a 3rd-party library without CMake

Drop it under libs/somelib/ with src/ and/or include/ subfolders — auto collection picks those up with no CMakeLists at all. Add libs/ to .gitignore if you fetch it instead of vendoring.

Shipping a runtime file next to the app (DLL / dylib / metallib / data)

Some addons need a file to sit beside the final executable at runtime — a Windows sensor-SDK .dll, a macOS .dylib/framework, a compiled .metallib, a data blob. From a custom CMakeLists.txt, register it with:

tc_addon_bundle_file(<path> [MACOS_DEST <subdir>])

TrussC copies it to the right place per platform, into whatever app consumes your addon — no need to know the app's target name:

  • macOS: into YourApp.app/Contents/<MACOS_DEST> (default Resources; use Frameworks for dylibs/frameworks).
  • Windows / Linux: next to the executable (so the loader finds the DLL / .so).
# e.g. a sensor SDK DLL that must sit beside the .exe on Windows:
if(WIN32)
    tc_addon_bundle_file("${CMAKE_CURRENT_SOURCE_DIR}/libs/sensor/sensor.dll")
endif()
# e.g. a generated Metal library into the app's Resources:
tc_addon_bundle_file("${CMAKE_CURRENT_BINARY_DIR}/default.metallib" MACOS_DEST Resources)

Generated files work too (e.g. a metallib produced by add_custom_command) — just add_dependencies() your addon target on the generator so the file exists before the app's post-build copy runs. Real example: tcxSyphon compiles Syphon's shaders to default.metallib and bundles it this way.


6. Platform-specific code

  • macOS / iOS (Objective-C++): put .mm / .m files in src/. They're compiled on every platform, so wrap the platform-only code in #ifdef __APPLE__ (or TARGET_OS_*). See tcxIME / tcxVirtualCam.
  • Windows / Linux: same idea — guard with #ifdef _WIN32 / __linux__, or link platform libs in a custom CMakeLists.txt (if(WIN32) … endif()).
  • Web (Emscripten): pure C++ generally just works. Avoid blocking I/O.

Android: Java, res, and the manifest

An addon can ship Android Java helpers and resources — TrussC aggregates them into the consuming app's APK automatically:

tcxYourAddon/android/
├── java/        # *.java — compiled (javac → d8) and bundled into classes.dex
├── res/         # Android resources — passed to aapt via -S
└── manifest/    # *.xml — manifest fragments the addon REQUESTS (advisory)

The app/example may also ship its own android/java, android/res, and android/AndroidManifest.xml(.in). At build time TrussC collects the project's dirs plus every loaded addon's dirs, so tcxMidi/android/java/*.java and your app's own Java compile together. (Native .so code links normally on Android regardless — this is only about the Java/.dex + res side.)

Manifest — advisory, never auto-injected. The AndroidManifest.xml belongs to the app and stays fully under the app's control: ship android/AndroidManifest.xml (copied as-is) or android/AndroidManifest.xml.in (@TC_APP_PACKAGE@ / @TC_APP_LIB_NAME@ / @TC_APP_HAS_CODE@ expand via configure_file). The default minSdkVersion is 26 — bump it in your own manifest if your addon needs a higher API (e.g. native AMidi needs 29).

There is no manifest merger. If your addon needs a <uses-permission>, <uses-feature>, <receiver>, or <service>, drop those lines in android/manifest/*.xml. At configure time TrussC gathers all addon fragments into build-android/REQUIRED_MANIFEST_SNIPPET.xml and prints a notice — the user then copies in only the parts they want (they may deliberately drop a permission). So: ship the fragment AND mention it in your README. Keep <uses-feature> entries required="false" so you never block install on devices lacking the hardware.


7. Continuous integration & tests

This template ships a tiny .github/workflows/ci.yml that calls the TrussC-org reusable workflow:

jobs:
  build:
    uses: TrussC-org/ci-actions/.github/workflows/build-addon.yml@v1

That's the whole file — nothing to edit after renaming the addon. It:

  • auto-detects the addon name from the repo, so a renamed copy keeps working;
  • builds your addon and every example-*/ directory on the platforms from addon.json (default: macOS / Windows / Linux — see §4);
  • so a fresh copy of this template goes green out of the box (it just verifies the example compiles).

The shipped on: block runs CI on every branch (branches: ['**']) plus v* tags and main-targeted PRs — so a push never silently skips CI, and no branch name is "magic." It's cheap (the trusscli build is cached per repo and shared across branches). If your addon's CI is genuinely too heavy or low-value to run on every push, narrow it to branches: [main] (opt-out).

External SDKs (setup_script)

If your addon needs a vendor SDK that isn't an apt package (a depth camera, capture device, …), the reusable workflow can't install it for you — but it gives you a hook. Pass a setup_script: bash that runs on every platform just before the build, branches on $RUNNER_OS, downloads the SDK, and exports what the build needs via $GITHUB_ENV (e.g. CMAKE_PREFIX_PATH so find_package resolves it). Combine with addon.json "platforms" to drop unsupported OSes. See the ci-actions README and tcxOrbbec for a worked example.

Adding tests (optional, opt-in)

Behavioral tests are opt-in by the presence of a tests/ directory — this template ships none, so nothing runs until you add one. To opt in, create a normal TrussC console project under tests/ whose main() returns non-zero on failure:

tcxYourAddon/
└── tests/
    ├── addons.make        # lists this addon
    └── src/main.cpp        # int main() { ...assert...; return failures; }

CI then builds and runs it automatically (a non-zero exit fails the job), skipping it entirely when absent. See tcxPly/tests for a complete example. (Console only, so it runs headless on all CI runners — no window.)


8. Publishing your addon

No pull request needed — the registry discovers addons by GitHub topic:

  1. Push your repo to GitHub, public, not archived.
  2. Repo name must match tcx[A-Z]… (e.g. tcxYourAddon).
  3. Add the topic trussc-addon (repo → About → ⚙ → Topics).
  4. Keep addon.json at the repo root (its presence is the opt-in marker).
  5. Tag releases (git tag v0.1.0 && git push --tags) — the crawler records the latest tag as the version.

A scheduled crawl at TrussC-org/trussc-addons finds it within a day and regenerates the registry (registry.json on gh-pages), which trusscli reads.

Don't add the trussc-addon topic to a copy of this template unless it's a real, finished addon — otherwise the literal tcxTemplate would show up in everyone's registry.


License

This template is MIT. Edit LICENSES.md — put your name/year on the addon's own section, and add one ----separated section for each third-party library you bundle/fetch and each data asset you ship (state its source and its license). Keep the license field in addon.json set to your addon's own SPDX id ("MIT", etc.).

Convention: the file is LICENSES.md (plural) — addons routinely vendor deps and assets, so the file is built to list them all, not just the addon's own license.

About

Template for make TrussC addon

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages