Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

EPICS-env is a self-contained, reproducible build environment for EPICS Base and a curated set of EPICS modules, managed entirely through GNU Make. The repository lives at github.com/jeonghanlee/EPICS-env.

This book collects the user-facing guides for the environment:

For the build quick start, supported platforms, and the prebuilt distribution, see the repository README.

Cycle records (work register, test plans, carry decision records) are maintained separately under docs/ in the repository; they are working records, not part of this book.

Module Management

How to manage EPICS modules in this environment:

Add a Module

Scope

This document covers the operator procedure for adding an EPICS module to this repository.

Out of scope: module naming rules, dependency key rules, and configure type semantics are defined in module-management.md.

Procedure

  1. Define the module source and version in configure/RELEASE.
SRC_NAME_SNMP:=snmp
SRC_TAG_SNMP:=tags/v1.0.0.2j
SRC_VER_SNMP:=1.0.0.2j
  1. Override the generated Git URL in configure/CONFIG_MODS when the module is not hosted under github.com/epics-modules.
SRC_GITURL_SNMP:=$(SRC_URL_JEONGHANLEE)/$(strip $(SRC_NAME_SNMP))
  1. Declare the build dependencies and configure type in configure/CONFIG_MODS_DEPS.
snmp_DEPS:=null.base
snmp_CONF_TYPE:=auto
  1. Provide the configuration target.

For auto modules, configure/RULES_MODS_CONF_AUTO generates conf.* and conf.*.show from the declarations in configure/CONFIG_MODS_DEPS. The snmp example is an auto module, so it does not need an explicit rule in configure/RULES_MODS_CONFIG.

For custom modules, add the target to configure/RULES_MODS_CONFIG and keep the group target list and concrete rule synchronized.

  1. Regenerate module metadata and verify the new module targets.
make reconf.modules
make PRINT.SRC_GITURL_SNMP
make PRINT.snmp_CONF_TYPE
make -n conf.snmp
make -n build.snmp
  1. Initialize, configure, build, install, and link the module.
make init.modules
make conf.snmp
make conf.snmp.show
make build.snmp
make install.snmp
make symlink.snmp
make exist.modules LEVEL=0

Configure Type

<module>_CONF_TYPE classifies whether the module configuration can be generated from a simple pattern or must remain hand-written. auto modules use generated conf.* targets; custom modules remain explicit rules.

See module-management.md for the classification table and naming rules.

Module Management

Scope

This document defines the module naming, dependency declaration, configure type declaration, and validation conventions used by the EPICS-env Makefile system.

Out of scope: operator commands for adding a module are covered in Add a Module. Module-specific patches and external vendor setup are covered by each module’s existing configuration rules.

Data Flow

Module metadata flows through the build system in this order.

  1. configure/RELEASE declares SRC_NAME_*, SRC_TAG_*, and SRC_VER_*.
  2. configure/CONFIG_MODS generates configure/MODULESGEN.mk and SRC_PATH_MODULES.
  3. configure/CONFIG_MODS_DEPS declares <module>_DEPS and <module>_CONF_TYPE.
  4. configure/RULES_FUNC derives generated target names from SRC_PATH_MODULES.
  5. configure/RULES_MODS_CONF_AUTO generates conf.* and conf.*.show targets for auto modules.
  6. configure/RULES_MODS_CONFIG keeps explicit conf.* and conf.*.show targets for custom modules.

Module Keys

The module key used in CONFIG_MODS_DEPS must match the generated build target key. The key is derived from SRC_PATH_MODULES by removing a trailing -src path component and by reducing recsync-src/client to recsync.

$(patsubst %-src,%, $(patsubst %/client, %, $(dir)))

The sequencer module has three names in the system.

LayerName
Source module namesequencer
Generated build targetbuild.sequencer
Installed module symlinkseq

The dependency key is therefore sequencer_DEPS, not sncseq_DEPS or seq_DEPS.

Build Dependency Declarations

Each module declares its build prerequisites with <module>_DEPS.

asyn_DEPS:=null.base build.sequencer build.sscan build.calc

The prerequisite order is significant because it becomes the prerequisite order of the generated build.<module> target.

Modules with only EPICS Base as a prerequisite use null.base.

pcas_DEPS:=null.base

Configure Type Declarations

Each module declares exactly one configure type.

TypeMeaning
autoConfiguration needs only INSTALL_LOCATION and simple local flags.
customConfiguration needs module paths, vendor paths, source edits, or special files.

Current behavior: auto modules use generated conf.* and conf.*.show rules. custom modules remain hand-written in configure/RULES_MODS_CONFIG.

The generated rule writes INSTALL_LOCATION to CONFIG_SITE.local and optionally writes module-specific simple lines declared in configure/CONFIG_MODS_DEPS.

Global Module Settings

conf.release.modules writes the repository-level CONFIG_SITE.local before module configuration runs. That file provides settings that apply to modules whose upstream configure/CONFIG_SITE includes $(TOP)/../CONFIG_SITE.local.

CHECK_RELEASE = NO
PROD_LDFLAGS += -Wl,--enable-new-dtags

Do not duplicate these global settings in generated auto-module declarations. For example, pscdrv inherits CHECK_RELEASE = NO through its upstream configure/CONFIG_SITE, so pscdrv_CONF_SITE_LINES must not restate it.

Module-local CHECK_RELEASE = NO belongs only in explicit custom rules that rewrite configure/CONFIG_SITE, remove the upstream include path, or need a module-specific override that is not provided by the repository-level local file.

Current Classification

autocustom
MCoreUtilsasyn
autosavebusy
caPutLogcalc
ether_iplinStat
iocStatslua
pcasmca
pscdrvmeasComp
retoolsmodbus
snmpmotor
motorMotorSim
opcua
pmac
pvxs
recsync
scaler
sequencer
sscan
std
StreamDevice

Validation

CONFIG_MODS_DEPS validates configure type declarations at Make parse time. Every module derived from SRC_PATH_MODULES must declare <module>_CONF_TYPE, and the value must be either auto or custom.

define validate_conf_type
$(if $($(1)_CONF_TYPE),,$(error Missing $(1)_CONF_TYPE declaration))
$(if $(filter auto custom,$($(1)_CONF_TYPE)),,$(error Invalid $(1)_CONF_TYPE value: $($(1)_CONF_TYPE)))
endef

This makes missing declarations and spelling errors fail before any module configuration or build recipe runs.

Maintenance Rules

When adding or renaming a module, keep these declarations aligned.

  1. SRC_NAME_* defines the source repository name.
  2. SRC_PATH_MODULES defines the generated module key.
  3. <module>_DEPS must use the generated module key.
  4. <module>_CONF_TYPE must use the generated module key.
  5. auto modules get generated conf.* rules.
  6. custom modules must keep explicit conf.* rules in RULES_MODS_CONFIG.
  7. A custom module’s conf.* target joins MODS_ZERO_CUSTOM_VARS or MODS_ONE_VARS. MODS_ZERO_VARS is derived from MODS_ZERO_CUSTOM_VARS plus the generated auto targets and is never an edit point; replacing it with a literal list drops every auto module’s configure target.

Changing how these lists are built also changes what the operator guides describe. The guides that name them are Add a Module, Worked Example: Add pmac, and Remove a Module; check all three against the change.

New Module

I would like to add the following repository for the production environment.

https://github.com/DiamondLightSource/pmac

Edit configure/RELEASE

The commit is July 15, 2024 at the dls-master branch In configure/RELEASE

# https://github.com/DiamondLightSource/pmac
# 2024-07-15 dls-master
SRC_URL_PMAC:=https://github.com/DiamondLightSource/pmac
SRC_NAME_PMAC:=pmac
SRC_TAG_PMAC:=3d2e73f
SRC_VER_PMAC:=3d2e73f

Edit configure/CONFIG_MODS if the module URL is not github/epics-modules

SRC_GITURL_PMAC:=$(strip $(SRC_URL_PMAC))/$(strip $(SRC_NAME_PMAC))

Edit configure/CONFIG_MODS_DEPS

pmac_DEPS:=null.base build.asyn build.calc build.motor build.busy
  • Edit configure/RULES_MODS_CONFIG

Please consult XXXApp/src/Makefile to check its real dependency and add the proper configuration name in one of the following variables.

  • MODS_ZERO_CUSTOM_VARS : This module has only EPICS base dependency.
  • MODS_ONE_VARS : This module has multiple EPICS modules dependencies.

MODS_ZERO_VARS is not an edit point. It is derived from MODS_ZERO_CUSTOM_VARS plus the generated auto-module targets, so replacing it with a literal list drops the configure target of every auto module.

pmac has asyn, calc, motor, busy dependencies. So add conf.pmac into MODS_ONE_VARS

MODS_ONE_VARS:=conf.calc conf.asyn conf.modbus conf.lua conf.std conf.StreamDevice conf.busy conf.scaler conf.mca conf.pmac

Then, add conf.pmac and conf.pmac.show as follows

conf.pmac:
	@-rm -f $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE.linux-x86_64.Common
	@echo "INSTALL_LOCATION:=$(INSTALL_LOCATION_PMAC)"  > $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@echo "CHECK_RELEASE = NO"                         >> $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@echo "BUILD_IOCS = NO"                            >> $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@echo "USE_GRAPHICSMAGICK = NO"                    >> $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@echo "SSH ="                                      >> $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@echo "SSH_LIB ="                                  >> $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@echo "SSH_INCLUDE ="                              >> $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@echo "WITH_BOOST = NO"                            >> $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@echo "USR_LDFLAGS += -lssh2"                      >> $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	@-rm -f $(TOP)/$(SRC_PATH_PMAC)/configure/RELEASE.local.linux-x86_64
	@-rm -f $(TOP)/$(SRC_PATH_PMAC)/configure/RELEASE.linux-x86_64.Common
	@echo "ASYN=$(INSTALL_LOCATION_ASYN)"               > $(TOP)/$(SRC_PATH_PMAC)/configure/RELEASE.local
	@echo "BUSY=$(INSTALL_LOCATION_BUSY)"              >> $(TOP)/$(SRC_PATH_PMAC)/configure/RELEASE.local
	@echo "CALC=$(INSTALL_LOCATION_CALC)"              >> $(TOP)/$(SRC_PATH_PMAC)/configure/RELEASE.local
	@echo "MOTOR=$(INSTALL_LOCATION_MOTOR)"            >> $(TOP)/$(SRC_PATH_PMAC)/configure/RELEASE.local
	@echo "EPICS_BASE:=$(INSTALL_LOCATION_BASE)"       >> $(TOP)/$(SRC_PATH_PMAC)/configure/RELEASE.local
	
conf.pmac.show: conf.release.modules.show
	cat -b $(TOP)/$(SRC_PATH_PMAC)/configure/CONFIG_SITE
	cat -b $(TOP)/$(SRC_PATH_PMAC)/configure/RELEASE.local
  • Commands
make reconf.modules
make init.modules
make conf.pmac
make conf.pmac.show
     1	EPICS_BASE:=/home/jeonglee/epics/debian/10/e881cb1/base
     2	SUPPORT=
     1	CHECK_RELEASE = NO
     1	INSTALL_LOCATION:=/home/jeonglee/epics/debian/10/e881cb1/modules/scaler-c7c0bf9
     1	ASYN=/home/jeonglee/epics/debian/10/e881cb1/modules/asyn-4.41
     2	AUTOSAVE=/home/jeonglee/epics/debian/10/e881cb1/modules/autosave-5.10.2
make build.pmac
make install.pmac
make symlinks

Installation on different version module

This is a short instruction how we can install a different version of a module in the existing EPICS-env environment.

Procedure

  • Be in the EPICS-env
EPICS-env (master)$ pwd
/home/jeonglee/gitsrc/EPICS-env
  • Clone a module which one install independently.
EPICS-env (master)$ git clone https://github.com/epics-modules/measComp
EPICS-env (master)$ cd measComp
measComp (master)$ git checkout 2e779c4
measComp ((2e779c4...))$
  • Copy RELEASE.local and CONFIG_SITE.local from the existing module configuration
measComp ((2e779c4...))$ scp ../measComp-src/configure/RELEASE.local configure/
measComp ((2e779c4...))$ cat -b configure/RELEASE.local
     1	SNCSEQ=/usr/local/epics/rocky-8.5/7.0.6.1/modules/seq-2.2.8
     2	SSCAN=/usr/local/epics/rocky-8.5/7.0.6.1/modules/sscan-2-11-5
     3	CALC=/usr/local/epics/rocky-8.5/7.0.6.1/modules/calc-3.7.4
     4	ASYN=/usr/local/epics/rocky-8.5/7.0.6.1/modules/asyn-4.41
     5	AUTOSAVE=/usr/local/epics/rocky-8.5/7.0.6.1/modules/autosave-5.10.2
     6	BUSY=/usr/local/epics/rocky-8.5/7.0.6.1/modules/busy-1.7.3
     7	SCALER=/usr/local/epics/rocky-8.5/7.0.6.1/modules/scaler-c7c0bf9
     8	STD=/usr/local/epics/rocky-8.5/7.0.6.1/modules/std-3.6.2
     9	MCA=/usr/local/epics/rocky-8.5/7.0.6.1/modules/mca-89ddd38
measComp ((2e779c4...))$ scp ../measComp-src/configure/CONFIG_SITE.local configure/
measComp ((2e779c4...))$ cat -b configure/CONFIG_SITE.local
     1	INSTALL_LOCATION:=/usr/local/epics/rocky-8.5/7.0.6.1/modules/measComp-tc32
     2	LINUX_LIBUSB-1.0_INSTALLED = NO
     3	LINUX_NET_INSTALLED = NO
  • Check the existing module path
measComp ((2e779c4...))$ make -C ../ exist LEVEL=2 |grep meas
│   ├── measComp -> /usr/local/epics/rocky-8.5/7.0.6.1/modules/measComp-tc32
│   ├── measComp-tc32
  • Replace INSTALL_LOCATION with a different path
INSTALL_LOCATION:=/usr/local/epics/rocky-8.5/7.0.6.1/modules/measComp-2e779c4
  • Build / Install
measComp ((2e779c4...))$ sudo make
measComp ((2e779c4...))$ sudo make install
measComp ((2e779c4...))$ make -C ../ exist LEVEL=2 |grep meas
│   ├── measComp -> /usr/local/epics/rocky-8.5/7.0.6.1/modules/measComp-tc32
│   ├── measComp-2e779c4
│   ├── measComp-tc32
  • Enjoy!

Move a module back to the community epics-modules repository

Some time after moving to our own local forked version, one may want to go back to the community epics-modules repository. The configure/CONFIG_MODS and configure/RELEASE files should be changed.

RELEASE

Select the different tag or hash which one would like to use, for example,

-# github/jeonghanlee
+# github/epics-modules
 SRC_NAME_MEASCOMP:=measComp
-SRC_TAG_MEASCOMP:=tc32
-SRC_VER_MEASCOMP:=tc32
+SRC_TAG_MEASCOMP:=2e779c4
+SRC_VER_MEASCOMP:=2e779c4

CONFIG_MODS

We don’t need to define their SRC_GITURL if we want to switch epics-modules, because the default SRC_GITURL is epics-module repository url. Thus, one should comment out the existing module specific url. For example,

--- a/configure/CONFIG_MODS
+++ b/configure/CONFIG_MODS
@@ -33,7 +33,8 @@ SRC_GITURL_STREAM:=$(SRC_URL_PSI)/$(strip $(SRC_NAME_STREAM))
 SRC_GITURL_SNCSEQ:=$(SRC_URL_JEONGHANLEE)/$(strip $(SRC_NAME_SNCSEQ))
 SRC_GITURL_SNMP:=$(SRC_URL_JEONGHANLEE)/$(strip $(SRC_NAME_SNMP))
 SRC_GITURL_OPCUA:=$(SRC_URL_RALPH)/$(strip $(SRC_NAME_OPCUA))
-SRC_GITURL_MEASCOMP:=$(SRC_URL_JEONGHANLEE)/$(strip $(SRC_NAME_MEASCOMP))
+# Move to the epics-module repository
+# SRC_GITURL_MEASCOMP:=$(SRC_URL_JEONGHANLEE)/$(strip $(SRC_NAME_MEASCOMP))
 SRC_GITURL_MOTORSIM:=$(SRC_URL_MOTOR)/$(strip $(SRC_NAME_MOTORSIM))

Commands

rm -rf measComp-src
make init.modules
make conf.measComp
make build.measComp
make install.measComp
make exist

Remove a module

From time to time, we have to drop a specific module support due to the upstream repository maintenance and latest Linux compiler, and so on. Here we shortly show how to remove the existing module from the environment.

A module

pyDevSup was abandoned, and no usage case was found for the ALS-U project, and it is difficult to keep the modern Python environment compatibility, which is a typical and common issue on every Python application.

configure/RELEASE

Remove the following lines

## https://github.com/jeonghanlee/pyDevSup
## 2024-08-31
SRC_NAME_PYDEVSUP:=pyDevSup
SRC_TAG_PYDEVSUP:=796f7d7
SRC_VER_PYDEVSUP:=796f7d7

configure/CONFIG_MODS

The pyDevSup is from my own forked and updated repository, please remove the following line in CONFIG_MODS as well.

SRC_GITURL_PYDEVSUP:=$(strip $(SRC_URL_JEONGHANLEE))/$(strip $(SRC_NAME_PYDEVSUP))

configure/CONFIG_MODS_DEPS

Please remove the following line in CONFIG_MODS_DEPS

pyDevSup_DEPS:=null.base

configure/RULES_MODS_CONFIG

  • Remove conf.pyDevSup from MODS_ZERO_CUSTOM_VARS

    MODS_ZERO_VARS is derived from that list plus the generated auto-module targets, so deleting a name from the derived line has no effect and the module stays installed.

  • Remove conf.pyDevSup and conf.pyDevSup.show rules completely.

conf.pyDevSup:
	@echo "INSTALL_LOCATION:=$(INSTALL_LOCATION_PYDEVSUP)"        > $(TOP)/$(SRC_PATH_PYDEVSUP)/configure/CONFIG_SITE.local
	@echo "PYTHON:=python3"                                      >> $(TOP)/$(SRC_PATH_PYDEVSUP)/configure/CONFIG_SITE.local
	@$(PYTHON_CMD) $(TOP)/$(SRC_PATH_PYDEVSUP)/makehelper.py     >> $(TOP)/$(SRC_PATH_PYDEVSUP)/configure/CONFIG_SITE.local

conf.pyDevSup.show: conf.release.modules.show
	@echo "cat -b $(TOP)/$(SRC_PATH_PYDEVSUP)/configure/CONFIG_SITE.local"
	cat -b $(TOP)/$(SRC_PATH_PYDEVSUP)/configure/CONFIG_SITE.local

Module Dependency Audit Design

Scope

This document defines the design for auditing EPICS module build dependencies from source evidence.

Out of scope: dynamic ELF dependency checks are handled by tools/check_deps.bash. Module configure-type classification is covered in module-management.md.

Problem

configure/CONFIG_MODS_DEPS is the build-order source of truth for this repository, but those declarations are manually maintained. A useful Phase 4 validator must not only compare _DEPS against generated RELEASE.local files. It must inspect the upstream module source trees and collect evidence from Makefiles, database files, DBD files, protocol references, and source headers.

The first implementation should therefore be an audit command, not a hard failure gate. The audit reports evidence and confidence. A later check command can fail the build only after the evidence rules are stable.

Proposed Commands

make audit.module-deps
make audit.module-deps MODULE=calc
make audit.module-deps FORMAT=json
make check.module-deps

audit.module-deps produces a human-readable report and can also emit JSON. check.module-deps applies the strict policy gate and fails on configured mismatch classes.

Files

tools/audit_module_deps.bash
configure/RULES_MODS_AUDIT
configure/CONFIG_MODS_AUDIT
docs/src/module-management/module-dependency-audit.md

CONFIG_MODS_AUDIT owns project-specific maps and allowlists. The script owns source scanning and report generation. RULES_MODS_AUDIT exposes Make targets and keeps the audit workflow inside the existing configure/RULES layout.

Reference Basis

The EPICS build system uses configure/RELEASE top definitions to derive include paths, DBD paths, DB paths, library paths, and bin paths for downstream builds. Source files listed in EPICS Makefile source variables receive generated header dependencies during the build.

Those two behaviors define the audit model:

  1. A module dependency may be visible as a RELEASE.local macro.
  2. The same dependency may also be visible as a Makefile library, DBD include, DB include, protocol reference, or source header include.
  3. The audit must preserve source evidence because a single signal is not always enough to prove a required build dependency.

Input Model

The audit starts from repository metadata already used by the build system.

  • SRC_PATH_MODULES: defines module source directories and module keys.
  • MODS_INSTALL_LOCATIONS: maps module keys to install names.
  • <module>_DEPS: declares intended build prerequisites.
  • <module>_CONF_TYPE: separates generated and custom configuration paths.
  • configure/RULES_MODS_CONFIG: provides manually generated RELEASE.local content for custom modules.
  • Generated configure/RELEASE.local files: provide configured dependency macros after conf.*.

The script should obtain Make-expanded values through small make print-% queries rather than reimplementing the Make expressions in Bash. The implementation uses print-% (bare value, configure/RULES_VARS), not the neighboring PRINT.% rule, which prints name = value plus an origin line and would break value parsing.

Evidence Sources

The scanner should collect evidence from these source classes.

  • Makefile: *_LIBS, PROD_LIBS, LIB_LIBS, DBD +=, <name>_DBD +=, DB, DB_INSTALLS, and installed public headers declared through INC.
  • *.dbd: include "module.dbd" and recordtype(...) definitions.
  • *.db, *.template, *.substitutions: file "...", record(type,...), and protocol references in INP and OUT.
  • Startup command files: dbLoadRecords, dbLoadTemplate, and dbLoadDatabase references.
  • C and C++ source headers: #include <...> and #include "..." mapped through installed public headers declared by module Makefiles.
  • Generated config files: configure/RELEASE.local, configure/CONFIG_SITE.local, and module-specific CONFIG_SITE overrides.

RELEASE.local scanning treats module-like macros with non-empty path values as dependency evidence. Boolean or empty local options, such as CHECK_RELEASE=NO or module-specific feature switches, are configuration evidence and are not reported as dependencies.

Documentation, license files, changelogs, and README examples should be ignored by default. Test and example applications should be reported under a separate context because they may not describe default production dependencies.

Module Artifact Catalog

The audit needs a catalog that maps artifacts back to module keys.

ArtifactExample Mapping
Release macroASYN -> asyn, SNCSEQ -> sequencer, SSCAN -> sscan.
Build targetbuild.asyn -> asyn.
Install symlinkseq -> sequencer.
HeaderasynDriver.h -> asyn, pvxs/version.h -> pvxs.
DBD includecalcSupport.dbd -> calc.
DB filesave_restoreStatus.db -> autosave.
Library nameasyn -> asyn, autosave -> autosave.

The first catalog should be generated from the source tree where possible and completed with a small explicit alias table for names that cannot be inferred. The required explicit aliases are expected to include sequencer/SNCSEQ/seq and any module whose installed name, release macro, library name, or source key does not match exactly.

Evidence Classification

Each evidence item is classified before it is compared with _DEPS.

ClassMeaningDefault Check Behavior
requiredThe active build path references another module directly.Eligible for strict check.
probableEvidence maps to one module but may be optional.Report only at first.
optionalEvidence is in tests, examples, docs, disabled blocks, or conditional paths.Report only.
externalThe reference points to a vendor or system library outside EPICS modules.Report separately.
baseThe reference resolves to EPICS Base.Suppress unless verbose.
unknownThe reference cannot be mapped to a known module or external allowlist.Report as audit finding.

Phase 4A treats generated RELEASE.local module macros and DBD includes as required evidence. Active Makefile library references are reported as probable until the source scanner can prove that the referencing source file belongs to the default build path.

Source evidence expansion treats active DB and substitutions file references, startup database loads, and startup DBD loads as required evidence. Active database record types and source header includes are probable because they map to support ownership but do not always prove a required link or configured runtime dependency by themselves. The DB catalog includes source files, Makefile DB or DB_INSTALLS declarations, and the conventional name.substitutions -> name.db generated name so generated DB outputs can still be resolved by name. The source header catalog is limited to installed public headers declared through INC; path-qualified installed headers must match by the same path-qualified include name.

Path context is applied before source-type classification. For example, iocBoot startup loads remain optional even though active startup loads are treated as required.

Strict mode should initially fail only on required missing dependencies and unknown references from active build paths.

Path Context

The same text has different meaning depending on where it appears.

ContextExamplesTreatment
Active buildmodule Makefile, default DIRS, app src trees.Candidate required dependency.
Installed databaseDb, db, src/Db, installed templates.Candidate required or probable dependency.
IOC boot examplesiocBoot, example st.cmd.Optional unless default build installs it.
Tests and demostest, tests, unitTestApp, demoApp, example IOC apps.Optional by default.
Documentationdocs, documentation, README, CHANGELOG.Ignored by default.
Platform-specific sourceos/Linux, os/Darwin, os/vxWorks, os/default.Include only when selected.

The audit command should accept a platform selector, defaulting to the current host platform.

make audit.module-deps PLATFORM=Linux

Report Shape

The human report should be grouped by module.

Module Dependency Audit
Strict: NO
Source state: generated RELEASE.local files are used when present.
Platform: Linux

Module: calc
Declared: null.base build.sequencer build.sscan
Observed:
  required  sequencer  configure/RULES_MODS_CONFIG:...
  required  sscan      configure/RULES_MODS_CONFIG:...
  probable  asyn       calcApp/Makefile:...
Findings:
  none

The text report shows the original declared string for readability. The JSON report stores normalized module keys so downstream tooling does not need to remove null.base, strip build., or apply aliases again.

The report header records that generated RELEASE.local files are consumed when they exist. A clean tree that has not run conf.* may therefore report more declared-unobserved findings than a configured tree.

{
  "module": "calc",
  "declared": ["sequencer", "sscan"],
  "observed": [
    {
      "dependency": "sequencer",
      "class": "required",
      "source": "release-local",
      "path": "calc-src/configure/RELEASE.local",
      "line": 1
    },
    {
      "dependency": "sscan",
      "class": "required",
      "source": "release-local",
      "path": "calc-src/configure/RELEASE.local",
      "line": 2
    },
    {
      "dependency": "asyn",
      "class": "probable",
      "source": "make-libs",
      "path": "calcApp/Makefile",
      "line": 42
    }
  ],
  "findings": []
}

Comparison Rules

The audit compares normalized module keys.

  1. Remove null.base from declared dependencies.
  2. Convert build.<module> to <module>.
  3. Convert aliases to module keys through CONFIG_MODS_AUDIT.
  4. Compare declared dependencies against observed required evidence.
  5. Report probable evidence separately until the signal is promoted.
  6. Report unknown active evidence even when _DEPS is otherwise complete.

The audit must not remove or rewrite _DEPS. It only reports discrepancies.

Finding labels describe the observation direction.

FindingMeaning
undeclared-observedEvidence points to a module dependency that is not declared in _DEPS.
declared-unobserved_DEPS declares a dependency with no matching required evidence.
unknownActive evidence could not be mapped to a known module or allowlisted external.

Make Integration

configure/RULES_MODS_AUDIT should be included by configure/RULES_MODS after module metadata is available and before user extension rules.

.PHONY: audit.module-deps check.module-deps

AUDIT_MODULE_DEPS = bash $(TOP)/tools/audit_module_deps.bash --top $(TOP)

audit.module-deps:
	$(QUIET) $(AUDIT_MODULE_DEPS) --module "$(MODULE)" --format "$(FORMAT)" --platform "$(PLATFORM)"

check.module-deps:
	$(QUIET) $(AUDIT_MODULE_DEPS) --module "$(MODULE)" --format "$(FORMAT)" --platform "$(PLATFORM)" --strict

The final implementation should keep command lines readable in the Makefile. If the argument list grows, move defaults into CONFIG_MODS_AUDIT and pass the minimum required flags.

The script treats an empty MODULE value as all modules and an empty FORMAT value as the default human-readable text report. In strict mode, check.module-deps exits with code 2 when a strict finding is present.

Implementation Phases

Phase 4A: Inventory Report

Create tools/audit_module_deps.bash with read-only scanning and a JSON/text report. No hard failure except invalid input or unreadable source paths.

Minimum coverage:

  1. Parse module keys and declared _DEPS.
  2. Scan active Makefiles for library and DBD references.
  3. Scan generated RELEASE.local files when present.
  4. Scan DBD include statements.
  5. Emit unknown references and declared-vs-observed summary.

Phase 4B: Source Evidence Expansion

Add DB, template, substitutions, protocol, startup command, and source header scanners. Build the DB, DBD, protocol, record-type, and installed-header catalogs from module source trees and explicit aliases.

Phase 4C: Strict Check

Enable make check.module-deps after the inventory report is reviewed. The first strict policy should fail only on:

  1. undeclared-observed: required evidence points to an undeclared module dependency.
  2. unknown: active evidence cannot be mapped to a known module or allowlisted external token.

declared-unobserved findings and probable, optional, and external evidence remain report-only under the first strict policy.

Strict policy violations exit with code 2. Report output stays on stdout, including valid JSON when FORMAT=json; the strict failure summary is written to stderr.

Phase 4D: CI Integration

Add a strict-check aggregate target for CI and local release-style validation after the source scanners have at least one reviewed baseline report.

Representative Linux CI workflows use make github.check, which runs the existing github workflow sequence with check.module-deps inserted after conf and before build. Workflows that keep explicit init, conf, and build steps rely on the same source-level gate coverage from the representative jobs instead of repeating the audit in every matrix entry. Keep make github available as the original workflow, and keep audit.module-deps available for explanatory review output.

Run github.check as a serial aggregate target. The gate depends on conf completing first because generated RELEASE.local files are part of the audit evidence, and build must not start until check.module-deps passes.

Review Checklist

Reviewers should check these design points before implementation.

  1. The audit is source-evidence driven and does not treat _DEPS as the proof.
  2. The first command is report-only, so false positives do not block builds.
  3. check_deps.bash remains separate because it verifies ELF runtime paths.
  4. Alias mapping is explicit where module names diverge.
  5. Test, example, documentation, and platform-specific evidence do not become hard failures by default.
  6. Strict mode has a narrow first policy and can be expanded after baselines are reviewed.

Phase 4 Closure

Phase 4 is complete when check.module-deps passes with zero strict findings and representative Linux CI workflows use make github.check.

iocBoot evidence remains optional in Phase 4. The audit does not attempt to separate default-installed boot files from example or site-specific startup scripts.

Vendor and system dependencies remain outside the EPICS module dependency strict gate. References such as open62541, uldaq, net-snmp, and libevent should move to a separate vendor dependency audit if they need a policy gate.

The current vendor dependency record is documentation-only. It preserves where the dependency enters the build and where failure is expected to surface, without adding a separate audit target. The table records the main vendor dependencies already called out in the top-level README; it is not a complete system package inventory.

In the failure column, ELF/RPATH means post-build coverage by check_deps.bash. uldaq and open62541 come from their -env repositories; libevent comes from system packages.

ModuleVendorVerificationFailure
measCompuldaqVENDOR_ULDAQ_PATH; ULDAQ_DIR; ULDAQ_INCLUDElink; ELF/RPATH
opcuaopen62541OPEN62541_PATH; OPEN62541_LIB_DIRconf/link; ELF/RPATH
pvxslibeventsystem headers/libs; pvxs symlink layoutbuild/link; symlink; ELF/RPATH

Header evidence remains probable. The audit records installed public header ownership as supporting evidence, but it does not promote header includes to strict dependency proof.

EPICS Environment Parameters

Channel Access (CA) and EPICS Base configuration from the environment:

EPICS_CA_ADDR_LIST
EPICS_CA_CONN_TMO
EPICS_CA_AUTO_ADDR_LIST
EPICS_CA_REPEATER_PORT
EPICS_CA_SERVER_PORT
EPICS_CA_MAX_ARRAY_BYTES
EPICS_CA_AUTO_ARRAY_BYTES
EPICS_CA_MAX_SEARCH_PERIOD
EPICS_CA_NAME_SERVERS
EPICS_CA_MCAST_TTL
EPICS_CAS_INTF_ADDR_LIST
EPICS_CAS_IGNORE_ADDR_LIST
EPICS_CAS_AUTO_BEACON_ADDR_LIST
EPICS_CAS_BEACON_ADDR_LIST
EPICS_CAS_SERVER_PORT
EPICS_CA_BEACON_PERIOD
EPICS_CAS_BEACON_PERIOD
EPICS_CAS_BEACON_PORT
EPICS_BUILD_COMPILER_CLASS
EPICS_BUILD_OS_CLASS
EPICS_BUILD_TARGET_ARCH
EPICS_TZ
EPICS_TS_NTP_INET
EPICS_IOC_IGNORE_SERVERS
EPICS_IOC_LOG_PORT
EPICS_IOC_LOG_INET
EPICS_IOC_LOG_FILE_LIMIT
EPICS_IOC_LOG_FILE_NAME
EPICS_IOC_LOG_FILE_COMMAND
IOCSH_PS1
IOCSH_HISTSIZE
IOCSH_HISTEDIT_DISABLE

Effective PVA client configuration from the environment:

EPICS_PVA_ADDR_LIST
EPICS_PVA_AUTO_ADDR_LIST
EPICS_PVA_BROADCAST_PORT
EPICS_PVA_CONN_TMO
EPICS_PVA_INTF_ADDR_LIST
EPICS_PVA_NAME_SERVERS
EPICS_PVA_SERVER_PORT

Effective PVA server configuration from the environment:

EPICS_PVAS_AUTO_BEACON_ADDR_LIST
EPICS_PVAS_BEACON_ADDR_LIST
EPICS_PVAS_BROADCAST_PORT
EPICS_PVAS_IGNORE_ADDR_LIST
EPICS_PVAS_INTF_ADDR_LIST
EPICS_PVAS_SERVER_PORT

Archived Notes

The platform and site notes below are working records, kept at the docs/ top level of the repository. Each records a specific era of the environment; their full cleanup is parked for after the 1.3.0 release, so they are linked here rather than carried as book chapters.

  • Docker — 2020 exploration of running the environment inside a container (Debian Buster, EPICS 7.0.4).
  • macOS 11 on Apple Silicon — macOS 11 / M1-era notes on EPICS_HOST_ARCH detection and softIoc.
  • Libera cross-compilation — building for the Libera beam loss monitor with the linux-arm cross target; its companion scripts/build_base_libera.bash still exists in the repository.
  • ALS-U EPICS Environment — RC-era ALS-U site installation guide, with an exported PDF.