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:
- Module Management — add, configure, replace, and remove modules, and audit their dependencies.
- EPICS Environment Parameters — the environment variables recognized by EPICS Base and PVXS.
- Archived Notes — links to era-specific platform and site records kept outside the book.
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 — the operator procedure for adding a module to the build system.
- Module Management Conventions — naming, dependency declaration, configure types, and validation rules.
- Worked Example: Add pmac — a complete example of adding a module with a custom configuration rule.
- Use a Different Module Version — install another version of a module beside the existing one.
- Change a Module Repository URL — switch a module between a fork and the community repository.
- Remove a Module — retire a module from the environment.
- Module Dependency Audit — the design of
make check.module-depsand the dependency evidence model.
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
- 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
- Override the generated Git URL in
configure/CONFIG_MODSwhen the module is not hosted undergithub.com/epics-modules.
SRC_GITURL_SNMP:=$(SRC_URL_JEONGHANLEE)/$(strip $(SRC_NAME_SNMP))
- Declare the build dependencies and configure type in
configure/CONFIG_MODS_DEPS.
snmp_DEPS:=null.base
snmp_CONF_TYPE:=auto
- 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.
- 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
- 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.
configure/RELEASEdeclaresSRC_NAME_*,SRC_TAG_*, andSRC_VER_*.configure/CONFIG_MODSgeneratesconfigure/MODULESGEN.mkandSRC_PATH_MODULES.configure/CONFIG_MODS_DEPSdeclares<module>_DEPSand<module>_CONF_TYPE.configure/RULES_FUNCderives generated target names fromSRC_PATH_MODULES.configure/RULES_MODS_CONF_AUTOgeneratesconf.*andconf.*.showtargets forautomodules.configure/RULES_MODS_CONFIGkeeps explicitconf.*andconf.*.showtargets forcustommodules.
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.
| Layer | Name |
|---|---|
| Source module name | sequencer |
| Generated build target | build.sequencer |
| Installed module symlink | seq |
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.
| Type | Meaning |
|---|---|
auto | Configuration needs only INSTALL_LOCATION and simple local flags. |
custom | Configuration 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
auto | custom |
|---|---|
MCoreUtils | asyn |
autosave | busy |
caPutLog | calc |
ether_ip | linStat |
iocStats | lua |
pcas | mca |
pscdrv | measComp |
retools | modbus |
snmp | motor |
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.
SRC_NAME_*defines the source repository name.SRC_PATH_MODULESdefines the generated module key.<module>_DEPSmust use the generated module key.<module>_CONF_TYPEmust use the generated module key.automodules get generatedconf.*rules.custommodules must keep explicitconf.*rules inRULES_MODS_CONFIG.- A
custommodule’sconf.*target joinsMODS_ZERO_CUSTOM_VARSorMODS_ONE_VARS.MODS_ZERO_VARSis derived fromMODS_ZERO_CUSTOM_VARSplus 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.localandCONFIG_SITE.localfrom 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_LOCATIONwith 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.pyDevSupfromMODS_ZERO_CUSTOM_VARSMODS_ZERO_VARSis 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.pyDevSupandconf.pyDevSup.showrules 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:
- A module dependency may be visible as a
RELEASE.localmacro. - The same dependency may also be visible as a Makefile library, DBD include, DB include, protocol reference, or source header include.
- 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 generatedRELEASE.localcontent for custom modules.- Generated
configure/RELEASE.localfiles: provide configured dependency macros afterconf.*.
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 throughINC.*.dbd:include "module.dbd"andrecordtype(...)definitions.*.db,*.template,*.substitutions:file "...",record(type,...), and protocol references inINPandOUT.- Startup command files:
dbLoadRecords,dbLoadTemplate, anddbLoadDatabasereferences. - 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-specificCONFIG_SITEoverrides.
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.
| Artifact | Example Mapping |
|---|---|
| Release macro | ASYN -> asyn, SNCSEQ -> sequencer, SSCAN -> sscan. |
| Build target | build.asyn -> asyn. |
| Install symlink | seq -> sequencer. |
| Header | asynDriver.h -> asyn, pvxs/version.h -> pvxs. |
| DBD include | calcSupport.dbd -> calc. |
| DB file | save_restoreStatus.db -> autosave. |
| Library name | asyn -> 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.
| Class | Meaning | Default Check Behavior |
|---|---|---|
required | The active build path references another module directly. | Eligible for strict check. |
probable | Evidence maps to one module but may be optional. | Report only at first. |
optional | Evidence is in tests, examples, docs, disabled blocks, or conditional paths. | Report only. |
external | The reference points to a vendor or system library outside EPICS modules. | Report separately. |
base | The reference resolves to EPICS Base. | Suppress unless verbose. |
unknown | The 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.
| Context | Examples | Treatment |
|---|---|---|
| Active build | module Makefile, default DIRS, app src trees. | Candidate required dependency. |
| Installed database | Db, db, src/Db, installed templates. | Candidate required or probable dependency. |
| IOC boot examples | iocBoot, example st.cmd. | Optional unless default build installs it. |
| Tests and demos | test, tests, unitTestApp, demoApp, example IOC apps. | Optional by default. |
| Documentation | docs, documentation, README, CHANGELOG. | Ignored by default. |
| Platform-specific source | os/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.
- Remove
null.basefrom declared dependencies. - Convert
build.<module>to<module>. - Convert aliases to module keys through
CONFIG_MODS_AUDIT. - Compare declared dependencies against observed
requiredevidence. - Report
probableevidence separately until the signal is promoted. - Report unknown active evidence even when
_DEPSis otherwise complete.
The audit must not remove or rewrite _DEPS. It only reports discrepancies.
Finding labels describe the observation direction.
| Finding | Meaning |
|---|---|
undeclared-observed | Evidence points to a module dependency that is not declared in _DEPS. |
declared-unobserved | _DEPS declares a dependency with no matching required evidence. |
unknown | Active 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:
- Parse module keys and declared
_DEPS. - Scan active Makefiles for library and DBD references.
- Scan generated
RELEASE.localfiles when present. - Scan DBD include statements.
- 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:
undeclared-observed:requiredevidence points to an undeclared module dependency.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.
- The audit is source-evidence driven and does not treat
_DEPSas the proof. - The first command is report-only, so false positives do not block builds.
check_deps.bashremains separate because it verifies ELF runtime paths.- Alias mapping is explicit where module names diverge.
- Test, example, documentation, and platform-specific evidence do not become hard failures by default.
- 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.
| Module | Vendor | Verification | Failure |
|---|---|---|---|
measComp | uldaq | VENDOR_ULDAQ_PATH; ULDAQ_DIR; ULDAQ_INCLUDE | link; ELF/RPATH |
opcua | open62541 | OPEN62541_PATH; OPEN62541_LIB_DIR | conf/link; ELF/RPATH |
pvxs | libevent | system headers/libs; pvxs symlink layout | build/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_ARCHdetection and softIoc. - Libera cross-compilation
— building for the Libera beam loss monitor with the
linux-armcross target; its companionscripts/build_base_libera.bashstill exists in the repository. - ALS-U EPICS Environment — RC-era ALS-U site installation guide, with an exported PDF.