Customization
Dawn can be extended without forking the upstream tree. An out-of-tree project lives under a separate root and the build system pulls in its boards, classes, factories and apps. User factories take priority over the built-in ones, so the same mechanism that adds new types can also override default upstream objects.
A worked example is at examples/out-of-tree-demo/.
For a clean starting point outside the upstream repository, run
python -m dawnpy project new <path>. The scaffold includes a local
.dawnrc pointing at the upstream Dawn checkout, a minimal simulator
board overlay, an example descriptor, and the conventional
external/dawn_oot.cmake hook.
What can be extended
Boards
Any NuttX board layout under <oot>/boards/.... OOT boards source
$(DAWN_BOARDS_COMMON)/Kconfig for the fake-driver options
(see Fake Devices).
Configurations
defconfig + defconfig.dawn pairs in any
boards/<...>/configs/<name>/.
Descriptors
Hand-written C++ referenced by CONFIG_DAWN_APPS_EXAMPLE_DESC_PATH.
Resolves relative to $DAWN_OOT_ROOT.
IO / PROG / PROTO classes
Subclass CIOCommon / CProgCommon / CProtoCommon and route
through a user factory (IIOFactory / IProgFactory /
IProtoFactory, one per kind). The handler consults the user factory
first, then falls back to the built-in factory. Pick a class ID at or
above the user-reserved range (IO_CLASS_USER 500,
PROG_CLASS_USER / PROTO_CLASS_USER 511) for new types, or reuse
a built-in class ID to override the default implementation. Sources go
under <oot>/external/src/{io,prog,proto}/; register factories by
defining strong overrides of dawn::oot::user_*_factory() in
<oot>/external/dawn_oot_hooks.cxx (declared in
<dawn/oot.hxx>).
External apps
Extra NuttX apps under <oot>/external/apps/<name>/ using
nuttx_add_application(...). They build alongside Dawn’s apps and
surface as regular NSH builtins. Disabling CONFIG_DAWN_APPS_DAWN and
constructing CDawn from one of them also lets the project fully
replace the default apps_dawn lifecycle.
Public include path
<oot>/external/include/ is auto-added to dawn’s PUBLIC include path.
Reference layout
Only boards/ is required for dawnpy to detect an OOT project root.
external/ remains optional and is needed only when the project adds
OOT classes, apps, or factory overrides. The rest is convention.
my-dawn-project/
├── boards/
│ └── <arch>/<chip>/<board>/
│ ├── CMakeLists.txt
│ ├── Kconfig # sources DAWN_BOARDS_COMMON
│ ├── src/ # board bringup C
│ └── configs/<defconfig>/
│ ├── defconfig # NuttX defconfig
│ └── defconfig.dawn # Dawn-specific overrides
├── external/
│ ├── dawn_oot_hooks.cxx # user factory registration
│ ├── apps/ # optional: user NuttX apps
│ │ ├── Kconfig # sourced by dawn/apps/Kconfig
│ │ ├── CMakeLists.txt # sourced by dawn_oot_link_apps()
│ │ └── <appname>/
│ │ ├── Kconfig
│ │ ├── CMakeLists.txt # nuttx_add_application(NAME ...)
│ │ └── <appname>_main.c
│ └── src/
│ ├── io/ # custom IO classes + factory
│ ├── prog/ # custom PROG classes + factory
│ └── proto/ # custom PROTO classes + factory
└── descriptors/
└── demo.cxx # hand-written C++ descriptor
If the project is built outside the upstream Dawn checkout, add a
.dawnrc at the project root so dawnpy can persist the upstream
path and optional relocated NuttX trees:
[paths]
dawn_root = "../dawn-src"
nuttx_dir = "../vendor/nuttx"
nuttx_apps_dir = "../vendor/apps"
[project]
oot = true
types_from = "dawnpy_types.py"
The build seam (DAWN_OOT_ROOT)
When dawnpy detects that the requested defconfig lives in an OOT project,
it exports DAWN_OOT_ROOT=<oot project root> into the cmake/Kconfig
environment. Three consumers act on it:
dawn_oot_link()(fromdawn/cmake/dawn_oot.cmake, called bydawn/src/CMakeLists.txt) includes the explicit hook file passed in asDAWN_OOT_CMAKE_FILEand calls the OOT hookdawn_oot_user_link_dawn(dawn)so user class implementations can link into the dawn library through normal CMake logic.dawn/apps/dawn/CMakeLists.txtlooks forDAWN_OOT_CMAKE_FILE. If it is set, thedawn_oot_user_setup_app(apps_dawn)hook can attach any required C++ files, including a factory-registration translation unit that provides strong overrides of thedawn::oot::user_*_factory()hooks declared in<dawn/oot.hxx>.dawn_oot_link_apps()(also fromdawn_oot.cmake, called bydawn/apps/CMakeLists.txt) includes the same hook file fromDAWN_OOT_CMAKE_FILEand calls the OOT hookdawn_oot_user_link_apps()so user apps can be registered through normal CMake logic. Separately, dawnpy exportsDAWN_EXTENSION_APPS_KCONFIGwhen<oot>/external/apps/Kconfigexists, anddawn/apps/Kconfigorsource-s that explicit file path.
Adding IO / PROG / PROTO classes
The same recipe applies to all three kinds. Use a user-reserved class ID to add a brand-new type, or a built-in class ID to override the default implementation (the user factory wins).
Add a header + source pair under
external/src/<kind>/subclassingCIOCommon/CProgCommon/CProtoCommon.Add a factory class. Switch on
desc.getObjectCls()andreturn new MyClass(desc)for IDs you own;return nullptrfor the rest so the built-in factory takes over.In
external/src/<kind>/CMakeLists.txtcalltarget_sources(dawn PRIVATE ...)for both class and factory, plustarget_include_directories(dawn PUBLIC ...)for the header path.In
external/src/<kind>/Kconfigdeclare a guard option (DAWN_OOT_..._IOetc.). Source it from your boardKconfigvia$(DAWN_OOT_ROOT):source "$(DAWN_BOARDS_COMMON)/Kconfig" source "$(DAWN_OOT_ROOT)/external/src/io/Kconfig" source "$(DAWN_OOT_ROOT)/external/src/prog/Kconfig" source "$(DAWN_OOT_ROOT)/external/src/proto/Kconfig"
Register the factories in a normal C++ translation unit such as
external/dawn_oot_hooks.cxxby defining strong overrides of the three weak hooks declared in<dawn/oot.hxx>. Then attach that file from your OOT CMake hook file insidedawn_oot_user_setup_app()so the strong definitions beat the weak defaults from the dawn library:#include "dawn/oot.hxx" #include "my_io_factory.hxx" #include "my_prog_factory.hxx" #include "my_proto_factory.hxx" namespace dawn::oot { IIOFactory *user_io_factory(void) { static my::CIOMyFactory s; return &s; } IProgFactory *user_prog_factory (void) { static my::CProgMyFactory s; return &s; } IProtoFactory *user_proto_factory(void) { static my::CProtoMyFactory s; return &s; } }
App lifecycle hooks
Alongside the factory overrides, <dawn/oot.hxx> declares four optional
weak-default lifecycle hooks. OOT projects override any subset by adding
strong definitions to the same external/dawn_oot_hooks.cxx (no
extra CMake wiring needed – the file is already attached via
dawn_oot_user_setup_app()). Default implementations in
dawn/src/oot.cxx are no-ops, so leaving them alone preserves current
behaviour.
Hook |
Where it fires |
Failure handling |
|---|---|---|
|
|
Negative |
|
|
Negative |
|
|
|
|
|
Negative |
user_init and user_post_load are tied to the default apps_dawn
entry point. user_on_idle and user_pre_shutdown fire from
CDawn::start() and therefore also apply to any external app that
constructs and drives CDawn directly.
#include "dawn/oot.hxx"
namespace dawn::oot {
int user_init(void) { /* board fixup, log sink */ return OK; }
int user_post_load(CDawn *dawn) { /* inspect objects */ return OK; }
void user_on_idle(CDawn *dawn) { /* periodic work */ sleep(1); }
int user_pre_shutdown(CDawn *dawn) { /* persist state */ return OK; }
}
External apps
OOT projects can ship arbitrary NuttX applications alongside Dawn. They
live under <oot>/external/apps/<name>/, are built by Dawn’s apps
phase, and appear as regular NSH builtins.
<oot>/external/apps/CMakeLists.txtis the entry point. It typicallyadd_subdirectory()-s one or more<appname>/directories.Each
<appname>/CMakeLists.txtcallsnuttx_add_application(NAME <appname> SRCS ...).<oot>/external/apps/Kconfigis auto-sourced bydawn/apps/Kconfigviaorsource. It typicallysource-s each<appname>/Kconfig.
To fully replace the default apps_dawn, disable
CONFIG_DAWN_APPS_DAWN and have your own external app construct
CDawn(&io, &prog, &proto) directly. Use this when you need a
non-trivial main loop, multiple CDawn instances, or app-level state
that does not fit the standard lifecycle. The minimal template mirrors
dawn/apps/dawn/dawn_main.cxx: parse args (if any), call
dawn_board_init(), register descriptor slot 0 with
CDevDescriptor::regDescriptor(), fill the CRC if required, call
CDawn::load_descriptor() then CDawn::start().
Descriptor formats
- C++ (recommended for OOT):
Set
CONFIG_DAWN_APPS_EXAMPLE_DESC_FORMAT_CXX=yand pointCONFIG_DAWN_APPS_EXAMPLE_DESC_PATHat a hand-written.cxxrelative to your OOT root (apps_dawnadds$DAWN_OOT_ROOTto its include path). User classes appear via theirobjectId(...)static helpers like any built-in class.- YAML:
Works for built-in Dawn types and for user types registered via the
TypeRegistrationextension API. The simplest route is to drop adawnpy_types.pyfile in your OOT project and point the dawnpy CLI at it with the global--types-fromflag:python -m dawnpy --types-from examples/out-of-tree-demo/dawnpy_types.py \ build /tmp/oot examples/out-of-tree-demo/boards/.../nsh_user_shell
No
pip installrequired. For vendor SDKs distributed to multiple downstreams, the sameTypeRegistrationcan be exposed as adawnpy.extensions:descriptor_typesentry-point inpyproject.toml. See Out-of-Tree API Reference for both paths and the API.Custom per-type config items go on the TypeInfo’s
config_fields(ConfigFieldor dict). See Out-of-Tree API Reference. Field shapes that don’t fit the existing generators require the C++ descriptor path.