Oberon RTK

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):

  1. parses each source module to extract its name, IMPORT list, and CONST lines

  2. 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

  3. drops imports between sources in the same group (the referenced module is being merged into the same output)

  4. rewrites imports from sources in the other group to the consolidated module name – eg. a _DEV source importing RCC_SYS becomes an import of SYS in the merged DEV.mod

  5. rewrites qualified references in the CONST lines to match: intra-group references lose the qualifier (RCC_SYS.X -> X); cross-group references take the consolidated name (RCC_SYS.X -> SYS.X)

  6. 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.mod and *_DEV.mod source files. All source modules for one MCU live in a single directory; subdirectories are not searched.
output_dir
directory for the generated SYS.mod and DEV.mod. Existing files of those names are overwritten. The directory must already exist.

Options

-v, --verbose
verbose output. Print each source file read, its CONST line count and its imports, and note skipped sources (empty CONST).

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 recognisable MODULE <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

Device Modules

Last updated: 25 May 2026