merge-defs
Merge per-device Oberon definition modules into consolidated SYS.mod and DEV.mod
Name
merge-defs – merge per-device definition modules into consolidated modules
Synopsis
merge-defs source_dir output_dir [-v]
Description
merge-defs reads per-peripheral and per-subsystem Oberon
definition modules from a source directory and merges their CONST
sections into two consolidated output modules. Source files are
grouped by name suffix:
| Source files | Output module |
|---|---|
*_SYS.mod |
SYS.mod |
*_DEV.mod |
DEV.mod |
The split reflects how the framework partitions an MCU's
peripherals: _SYS modules describe devices used to configure and
run the MCU itself (clocks, power, resets), _DEV modules describe
devices used by the control program (UART, SPI, timers, GPIO). See
Device Modules for the rationale.
Without this consolidation step, every Astrobe Oberon program
importing more than a handful of peripherals would carry a long
IMPORT list of definitions-only modules. After merge-defs,
programs import just SYS and DEV.
What merge-defs does
For each group (_SYS, _DEV):
-
parses each source module to extract its name,
IMPORTlist, andCONSTlines -
sorts the sources by intra-group import dependencies (topological order, alphabetical within each level) so that referenced constants are declared before their users in the merged output
-
drops imports between sources in the same group (the referenced module is being merged into the same output)
-
rewrites imports from sources in the other group to the consolidated module name – eg. a
_DEVsource importingRCC_SYSbecomes an import ofSYSin the mergedDEV.mod -
rewrites qualified references in the
CONSTlines to match: intra-group references lose the qualifier (RCC_SYS.X->X); cross-group references take the consolidated name (RCC_SYS.X->SYS.X) -
emits the merged module, with each contributing source preceded by a
(* == ModName == *)separator comment
External imports (BASE, PPB, anything that is not a *_SYS or
*_DEV source in the input directory) are passed through unchanged
in the order first seen.
Source module shape
Each source module is a standard Oberon definition module with a
CONST section. A typical source looks like:
MODULE UART_DEV;
(** UART device definitions for STM32U585. **)
IMPORT BASE, RCC_SYS;
CONST
USART1_BASE* = BASE.USART1_BASE;
USART1_BC_reg* = RCC_SYS.RCC_APB2ENR;
(* ... *)
END UART_DEV.
Blank lines and standalone comment lines inside the CONST section
are dropped during the merge. Inline trailing comments on constant
lines are preserved verbatim.
A source with an empty CONST section is skipped silently
(or with a verbose message).
Arguments
- source_dir
- directory containing the
*_SYS.modand*_DEV.modsource files. All source modules for one MCU live in a single directory; subdirectories are not searched. - output_dir
- directory for the generated
SYS.modandDEV.mod. Existing files of those names are overwritten. The directory must already exist.
Options
- -v, --verbose
- verbose output. Print each source file read, its
CONSTline count and its imports, and note skipped sources (emptyCONST).
Output
For each non-empty group, merge-defs writes one module file to output_dir:
MODULE DEV;
(** Generated by merge-defs. Do not edit. **)
IMPORT BASE, SYS;
CONST
(* == GPIO_DEV == *)
GPIO_BASE* = BASE.GPIO_BASE;
(* ... *)
(* == UART_DEV == *)
USART1_BASE* = BASE.USART1_BASE;
USART1_BC_reg* = SYS.RCC_APB2ENR;
(* ... *)
END DEV.
The header line (** Generated by merge-defs. Do not edit. **)
marks the output as tool-managed. The source modules in
source_dir are the source of truth; the output is regenerated
whenever a source changes.
A one-line summary is printed on standard output for each group written:
merge-defs: cfg/DEV.mod (12 sources)
merge-defs: cfg/SYS.mod (8 sources)
Examples
Standard project layout
Source modules live in a src/ subdirectory of the MCU's base/
directory; the consolidated SYS.mod and DEV.mod are written one
level up:
python merge-defs.py lib/v3.1/mcu/rpi/rp2350/base/src lib/v3.1/mcu/rpi/rp2350/base
Verbose run
python merge-defs.py cfg/src cfg -v
read: GPIO_DEV.mod (24 lines, imports: BASE)
read: RCC_SYS.mod (16 lines, imports: BASE)
read: UART_DEV.mod (32 lines, imports: BASE, RCC_SYS)
...
merge-defs: cfg/DEV.mod (5 sources)
merge-defs: cfg/SYS.mod (3 sources)
Diagnostics
The tool exits with status 0 on success and status 1 on any error. Common error conditions:
-
source directory not found: <path> -
output directory not found: <path> -
no *_SYS.mod or *_DEV.mod files in <path> -
cannot parse MODULE name in <file>– the source file does not start with a recognisableMODULE <name>;header -
circular dependency among: <names>– two or more source modules in the same group import each other, directly or transitively
Notes
The merge is purely textual on the CONST section. merge-defs
does not validate that constants exist on the right-hand side, that
types match, or that the merged module compiles. The Astrobe
compiler does that on the next build.
Each output module is a single flat namespace. Source-level module
names survive only as (* == ModName == *) separator comments;
they have no semantic effect after merging. Two source modules
in the same group with overlapping constant names will collide in
the output and the Astrobe compiler will reject the merged module.
See Also
Last updated: 25 May 2026