Custom modules
A custom module is an ordinary NSX module that NSX’s registry does not pin. It has the same manifest, the same CMake contract and the same resolution behavior as a packaged one. The only difference is that you say where it comes from.
Write one when you have firmware you want to reuse across apps or share with a colleague.
For a change you only need in one app, plain CMake in that app’s CMakeLists.txt is less
work.
Scaffold it
Section titled “Scaffold it”nsx module init my-sensor --type algorithm --summary "Accelerometer feature extraction"That writes a module directory with a manifest, CMake, a header and a source stub, ready
to build. The flags seed the manifest: --type picks the module class, --dependency,
--board, --soc and --toolchain are each repeatable and declare what the module needs
and what it claims to work with. Compatibility defaults to * for boards and SoCs and to
arm-none-eabi-gcc for toolchains, so tighten those to what you have actually built.
The eight module types are algorithm, backend_specific, board, portable_api,
runtime, sdk_provider, soc and tooling. The module
model explains what each one means and which
rules NSX enforces per type.
The manifest
Section titled “The manifest”nsx-module.yaml is the contract. Every field, required and optional, is on the generated
nsx-module.yaml reference, so this page
only covers the parts that catch people out.
build.cmake.packageandbuild.cmake.targetsare how the rest of the build reaches your code. The package name is the module name with hyphens turned into underscores:my-sensorbecomesmy_sensor. The conventional target is the package name under thensx::namespace, with annsx_prefix dropped if present:my-sensorgivesnsx::my_sensor,nsx-timergivesnsx::timer.nsx module initwrites both for you.depends.requiredis resolved;depends.optionalis not. Optional dependencies are searchable metadata that tell a reader what your module can work with. Nothing installs them, but when an app adds one, NSX adds it to the build before your module, so yourCMakeLists.txtcan checkif(TARGET nsx::<dep>). This applies to registry modules; NSX does not read a local or vendored module’s optional dependencies when ordering.compatibilityis enforced. NSX rejects an app that pairs your module with a board, SoC or toolchain the manifest does not list. Declaring*everywhere makes the check useless and makes the catalog page misleading.- The semantic fields are how anyone finds you.
summary,capabilities,use_cases,anti_use_cases,agent_keywords,example_refsandcomposition_hintsare optional, and they are whatnsx module searchmatches against and what the catalog page renders.anti_use_casesis worth writing: saying what a module is wrong for saves more time than another sentence about what it is right for.
Check your work before an app ever sees it:
nsx module validate ./my-sensornsx module validate ./my-sensor --jsonThe CMake surface
Section titled “The CMake surface”Your module declares a package and one or more targets; NSX generates the wiring that makes them findable and links them into the app. Keep the target’s public include directories and link dependencies honest, because that is the only description the app has of how to consume you.
Dependencies belong in both places. The manifest tells NSX what to resolve and vendor; the
CMake target_link_libraries tells the compiler and linker what to use. Declaring a
dependency in only one of the two produces either a missing source tree or a missing
symbol, depending on which half you forgot.
Pointing an app at it
Section titled “Pointing an app at it”Three ways, by how much you want NSX to manage.
A path you are actively editing
Section titled “A path you are actively editing”nsx module add my-sensor --local --path ../my-sensornsx.yml records a source.path. Every nsx sync mirrors that directory into
modules/my-sensor/, so your edits show up in the next build. nsx.lock records a content
hash of the source directory, which means --frozen will notice when it changes. This is
the mode for developing a module and an app together.
Sources you want in your repository
Section titled “Sources you want in your repository”nsx module add my-sensor --vendoredmodules/my-sensor/ becomes yours: committed to your repository, never re-fetched, never
overwritten by nsx sync. The lock records only a content hash so drift is still
detectable. Use this when the app must build with no network and no second repository.
A repository, or a fork of a packaged module
Section titled “A repository, or a fork of a packaged module”nsx module register my-sensor \ --metadata ../my-sensor/nsx-module.yaml \ --project my-sensor \ --project-url https://github.com/you/my-sensor.git \ --project-revision v0.1.0This writes an app-local registry entry under module_registry in nsx.yml and vendors
the module in. From then on it resolves like any registry module: pinned to a revision,
re-fetched by nsx sync, reported by nsx outdated.
The same command with --override replaces where a module NSX already pins comes from,
which is how you test a fork of a packaged module without changing NSX. App-local entries
take precedence over the packaged registry, and the override lives in your app’s manifest,
so anyone cloning the app gets the fork too. --project-local-path vendors from a
filesystem path instead of a git URL, and --dry-run shows the manifest change without
writing it.
Rules NSX enforces
Section titled “Rules NSX enforces”- No dependency cycles.
Dependency cycle detected at module '<name>'is a manifest problem, not an app problem. - At most one
sdk_providermodule in an app. See SDK foundation and overrides. - A board module depends on exactly one SoC module.
- Compatibility is checked at resolution time, so a mismatch fails
nsx lock, not the compiler.
Publishing it more widely
Section titled “Publishing it more widely”Getting a module into the packaged registry, so that other people’s apps can add it by name, is a change to NSX itself. See Contribute a module.