| layout | default |
|---|---|
| title | Contributing a Module |
| nav_order | 2 |
To add your module to the epics-modules organization:
- Create an issue in your module's repository mentioning
@epics-modules/admins. - The module must include a
LICENSEfile at the top level. The synApps license (BSD-style, University of Chicago / UC Regents) is used by most modules in the collection, but other open-source licenses are acceptable. - An active maintainer is expected.
The rest of this page describes conventions followed by many modules in the collection. They are not requirements, but following them makes your module easier for others to build, integrate, and contribute to.
Beyond what makeBaseApp.pl generates, modules in the collection
typically add the following directories:
mymodule/
.ci/ ci-scripts submodule (see CI section)
.github/workflows/ GitHub Actions CI workflow
configure/ standard EPICS build configuration
docs/ GitHub Pages documentation
iocs/ separated IOC applications
mymoduleApp/ module source (Db/, src/)
Makefile
The *App/ directory name should match the module name
(e.g., busyApp/ for the busy module).
IOC application Makefiles must list the DBD files and libraries provided by each support module they use. When a module adds, removes, or renames a library or DBD file, every consuming IOC Makefile must be updated manually. The cfg system lets each module declare its own build products so that IOC applications can discover them automatically.
The CONFIG_MODULE template handles recursive dependency resolution.
If module A depends on module B, and both have cfg-deps, an IOC that
uses module A automatically discovers module B's build products as
well.
Each module needs five things:
-
MODULE = <NAME>inconfigure/CONFIG_SITE(must match the variable name used in RELEASE files):MODULE = AUTOSAVE -
A
CONFIG_MODULE@template file inconfigure/. This file is identical for every module -- copy it verbatim from any module that has it (e.g., busy). -
A declaration file (
<MODULE>_DEPS) in the module's source directory (e.g.,asApp/src/AUTOSAVE_DEPS). -
Additions to
configure/Makefile:CFG += CONFIG_MODULE EXPAND += CONFIG_MODULE@ EXPAND_VARS += MODULE=$(MODULE)
-
Install the declaration file from the source Makefile (e.g.,
asApp/src/Makefile):CFG += AUTOSAVE_DEPS
A declaration file lists the DBD files and libraries the module provides to IOC applications.
Basic -- a module with a single library and DBD:
# asApp/src/AUTOSAVE_DEPS
AUTOSAVE_IOC_DBDS = asSupport.dbd
AUTOSAVE_IOC_LIBS = autosaveConditional -- a module with optional components:
# sscanApp/src/SSCAN_DEPS
SSCAN_IOC_DBDS = sscanSupport.dbd
SSCAN_IOC_LIBS = sscan
ifdef SNCSEQ
SSCAN_IOC_DBDS += sscanProgressSupport.dbd
SSCAN_IOC_LIBS += scanProgress
endifDeclaration files are Make fragments evaluated inside an
ifdef T_A guard, so OS_CLASS, T_A, and ifdef MODULE
conditions are all available for platform-specific or
dependency-gated declarations.
Modules like motor that contain independently-maintained driver submodules should not hard-code every submodule's products in a single declaration file. Instead, the parent declaration file wildcard-includes fragment files that each submodule installs:
Parent (motorApp/MotorSrc/MOTOR_DEPS):
MOTOR_IOC_DBDS = motorSupport.dbd devSoftMotor.dbd
MOTOR_IOC_LIBS = motor softMotor
# Auto-include all submodule declaration fragments
-include $(_MOTOR_DIR)/MOTOR_*_DEPSEach submodule installs a small fragment (e.g.,
modules/motorNewport/newportApp/src/MOTOR_NEWPORT_DEPS):
MOTOR_IOC_DBDS += devNewport.dbd
MOTOR_IOC_LIBS += NewportThe submodule's Makefile just needs CFG += MOTOR_NEWPORT_DEPS.
When submodules are added or removed, no central file needs updating -- the wildcard automatically discovers whatever fragments are installed. Submodule fragments can use the same platform and dependency guards as regular declaration files.
IOC Makefiles use per-module variables to select which modules to include:
$(DBD_NAME)_DBD += $(AUTOSAVE_IOC_DBDS)
$(DBD_NAME)_DBD += $(MOTOR_IOC_DBDS)
$(PROD_NAME)_LIBS += $(MOTOR_IOC_LIBS)
$(PROD_NAME)_LIBS += $(AUTOSAVE_IOC_LIBS)
$(PROD_NAME)_LIBS += $(EPICS_BASE_IOC_LIBS)Most modules use epics-base/ci-scripts
as a .ci/ git submodule, with a GitHub Actions workflow in
.github/workflows/.
The test matrix should cover at minimum:
- Linux (gcc)
- macOS (clang)
- Windows (MSVC, static build)
Modules should be tested against both stable releases and master branches of their dependencies. This catches integration issues early and validates that cfg-deps declarations stay current.
A typical workflow structure:
on:
push:
branches: [master]
pull_request:
env:
SETUP_PATH: .github/workflows:.ci
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-latest
set: linux-stable
- os: ubuntu-latest
set: linux-master
- os: macos-latest
set: macos
- os: windows-latest
set: windows
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- run: python .ci/cue.py prepare
- run: python .ci/cue.py build
- run: python .ci/cue.py testThe SET files (e.g., linux-stable.set, linux-master.set) in
.github/workflows/ define which dependency versions to build
against.
- Tag releases as
R<major>-<minor>[-<patch>](e.g.,R1-7-4). - Create GitHub Releases from tags.
- Maintain release notes in
docs/(e.g.,docs/<module>ReleaseNotes.md).