EPICS Configuration Environment (EPICS-env)
EPICS-env builds one Experimental Physics and Industrial Control System (EPICS) base and a pinned set of 32 EPICS modules from source, and installs them as one tree per release, operating system (OS), and base version. The installed tree links its libraries relative to its own location, carries a shell environment script, and holds a set of common iocsh fragments for input/output controllers (IOCs).
A make target drives every action: fetching the sources, applying carried
upstream patches, configuring, building, installing, linking, and checking the
result. The three verification gates are make targets as well:
check.module-deps checks the module dependencies before the build, and
check.deps and check.env check the library search paths and the
environment script after the install.
Supported operating systems
The environment builds on six Linux OSs, each with one continuous integration
(CI) workflow: Debian 12, Debian 13, Rocky Linux 8, Rocky Linux 10, Ubuntu
24.04, and Ubuntu 26.04. Every workflow builds and installs the environment,
then runs check.env and check.deps on the installed tree. Every workflow
also runs check.module-deps after configuration and before the build.
Debian 12, Debian 13, and Rocky 8 run it through github.check; Rocky 10
and both Ubuntu workflows call it as a separate stage.
Supported platforms and CI lists the
container image and the build sequence of each workflow.
How to read this book
The pages follow the order in which you meet the system:
- The tutorial, Build and use your first EPICS environment, takes you from a fresh clone to a running IOC.
- The concept pages explain how the build pipeline, the module set, the installed tree, the verification gates, the patch carry, and the iocsh fragments work.
- The procedure pages each complete one task, such as a module bump or an upstream fix carry.
- The reference pages list exact values: make targets, variables, module pins, fragment macros, tools, and platforms.
- The glossary of EPICS-env terms defines each term the pages use.
Topics outside this book
The cross build of EPICS base and modules for a Libera target is outside this
book. It consists of scripts/build_base_libera.bash,
scripts/build_modules_libera.bash, the conf.modules.libera target, and
configure/os/CONFIG_SITE.linux-x86_64.linux-arm.
Build and use your first EPICS environment
In this lesson you build Experimental Physics and Industrial Control System (EPICS) base and its modules from source, and open a shell on the installed tree. You then start a soft input/output controller (IOC) that serves one process variable (PV). Last, you read and write that PV over Channel Access (CA) and pvAccess (PVA).
What you build in this lesson
At the end of the lesson you have:
- An installed tree at
<install_location>/1.4.0/debian-13/7.0.10, with EPICS base, every module, and the uldaq and open62541 vendor libraries. - A shell whose
PATHandLD_LIBRARY_PATHpoint at that tree. - A running IOC that serves the PV
tutorial:value, and a second shell that reads and writes it.
The lesson runs on a Debian 13 host. It needs:
- The host packages for EPICS base and the modules. The continuous integration (CI) workflows install them with https://github.com/jeonghanlee/pkg_automation; see Package setup in the OS workflows.
- Access to GitHub, and about 3.5 GB of free disk space for the sources, the build products, and the installed tree.
- An empty directory that you own, as the working directory of the lesson. The lesson starts there and keeps one shell open until the IOC stage.
The whole lesson takes about 12 to 15 minutes on a 20-core host; make build
is the longest step.
Fetch EPICS-env and set the install location
-
Clone EPICS-env and enter the clone:
git clone https://github.com/jeonghanlee/EPICS-env cd EPICS-env -
Set the install location in
configure/CONFIG_SITE.local:echo "INSTALL_LOCATION=<install_location>" > configure/CONFIG_SITE.local<install_location>is an absolute path that you can write to, such as/home/<user>/epics-lesson. Set it before running an action such asmake initormake build, because actions try to create this directory. Query-only invocations, such asmake print-INSTALL_LOCATION_EPICS, create no installation directory or generated configuration file. The default install location is${HOME}/epics. -
Print the path of the installed tree:
make print-INSTALL_LOCATION_EPICSThe output is:
<install_location>/1.4.0/debian-13/7.0.10The path adds the EPICS-env release
1.4.0, the operating systemdebian-13, and the EPICS base version7.0.10under your location. The stages below create this tree and fill it.
Build the vendor libraries
The measComp module links the uldaq library and the opcua module links the
open62541 library. In this lesson both libraries go into the vendor
directory of the installed tree.
-
Save the
vendorpath in a shell variable:VENDOR_PATH="$(make print-INSTALL_LOCATION_EPICS)/vendor" -
Point the two modules at that directory in
configure/RELEASE.local:echo "VENDOR_ULDAQ_PATH=${VENDOR_PATH}" > configure/RELEASE.local echo 'OPEN62541_PATH=\$$\$$\(\_OPEN62541_CONFIG_OPCUA\)/../../../vendor' >> configure/RELEASE.localType the second line exactly as shown. Its escapes let the installed
opcuaconfiguration find thevendordirectory relative to its own location. -
Look at the file:
cat configure/RELEASE.localThe file holds two lines:
VENDOR_ULDAQ_PATH=<install_location>/1.4.0/debian-13/7.0.10/vendor OPEN62541_PATH=\$$\$$\(\_OPEN62541_CONFIG_OPCUA\)/../../../vendor -
Build uldaq into the
vendordirectory:git clone https://github.com/jeonghanlee/uldaq-env ../uldaq-env echo "INSTALL_LOCATION=${VENDOR_PATH}" > ../uldaq-env/configure/CONFIG_SITE.local make -C ../uldaq-env init conf build install -
Build open62541 into the
vendordirectory:git clone https://github.com/jeonghanlee/open62541-env ../open62541-env echo "INSTALL_LOCATION=${VENDOR_PATH}" > ../open62541-env/configure/CONFIG_SITE.local make -C ../open62541-env init conf build install -
List the libraries:
ls "${VENDOR_PATH}/lib"The output lists the libraries of both packages:
cmake libopen62541.so libopen62541.so.1 libopen62541.so.1.3.15 libuldaq.a libuldaq.la libuldaq.so libuldaq.so.1 libuldaq.so.1.2.1 pkgconfigThe installed tree exists and holds its first directory,
vendor.
Fetch and patch the sources
-
Clone EPICS base and every module at its pinned tag or commit:
make init -
List the source trees:
ls -d *-srcThe output lists 33 source trees:
asyn-src autosave-src busy-src calc-src caPutLog-src epics-base-src ether_ip-src feed-core-src iocStats-src linStat-src lua-src mca-src MCoreUtils-src measComp-src modbus-src motorMotorSim-src motor-src opcua-src pcas-src pmac-src pscdrv-src pvxs-src pyDevSup-src QPC-src recsync-src retools-src rgamv2-src scaler-src sequencer-src snmp-src sscan-src std-src StreamDevice-srcEPICS base and 32 modules sit next to each other at the repository top. Module pins and dependencies lists the pin of each one. The list above follows the sort order of the
en_US.UTF-8locale; under another locale, such asC,lsorders the names differently. -
Apply the upstream fixes that EPICS-env carries:
make patchThe output starts with the first two patches of EPICS base:
Patching epics-base-src with the file : <clone>/patch/7.0.10-01-b2d2758-putnotify-type-check.p0.patch patching file modules/database/src/ioc/db/dbPutNotifyBlocker.cpp Patching epics-base-src with the file : <clone>/patch/7.0.10-pr0817-mbbi-cosv-aftc.p0.patch patching file modules/database/src/std/rec/mbbiRecord.c<clone>is the absolute path of your EPICS-env clone. EachPatchingline names one patch file from thepatchdirectory; the run applies 37 of them.
Configure, build, and install
-
Write the site configuration of base and every module:
make confThe command prints one empty line. It writes, among other files,
RELEASE.localat the repository top, which tells every module where the installed base is:cat RELEASE.localThe file holds two lines:
EPICS_BASE:=<install_location>/1.4.0/debian-13/7.0.10/base SUPPORT= -
Build base and every module:
make buildThis is the long step. Base installs into the tree as it builds, and each module installs when its build completes.
-
Install the setup and reset scripts, the
commonIocshfragments, and the version file, and complete the base and module installs:make install -
Create the unversioned module links:
make symlinksThe environment script finds the
pvxstools through the linkpvxs, so this step is required for the next stage. -
Look at the top level of the installed tree:
LC_ALL=C make exist LEVEL=1The output is:
<install_location>/1.4.0/debian-13/7.0.10 |-- .versions |-- base |-- modules |-- resetEpicsEnv.bash |-- setEpicsEnv.bash `-- vendor 4 directories, 3 filesLC_ALL=Cmakestreedraw its lines with American Standard Code for Information Interchange (ASCII) characters, as shown above. Repeating installation also leaves backups of replaced setup and reset scripts; those files add entries to the listing.
Open a shell on the installed tree
-
Source the environment script from the installed tree:
source <install_location>/1.4.0/debian-13/7.0.10/setEpicsEnv.bashThe script prints a summary that starts with these lines:
Set the EPICS Environment as follows: THIS Source NAME : setEpicsEnv.bash THIS Source PATH : <install_location>/1.4.0/debian-13/7.0.10 EPICS_BASE : <install_location>/1.4.0/debian-13/7.0.10/base EPICS_HOST_ARCH : linux-x86_64 EPICS_MODULES : <install_location>/1.4.0/debian-13/7.0.10/modules -
Find the IOC program that the lesson uses:
command -v softIocPVXThe output is:
<install_location>/1.4.0/debian-13/7.0.10/modules/pvxs/bin/linux-x86_64/softIocPVXsoftIocPVXcomes from thepvxsmodule. It serves its records over both CA and PVA; thesoftIocprogram of base serves CA only. -
Keep CA and PVA traffic of this lesson on the loopback interface:
export EPICS_CA_AUTO_ADDR_LIST=NO EPICS_CA_ADDR_LIST=127.0.0.1 EPICS_CAS_INTF_ADDR_LIST=127.0.0.1 export EPICS_PVA_AUTO_ADDR_LIST=NO EPICS_PVA_ADDR_LIST=127.0.0.1 EPICS_PVAS_INTF_ADDR_LIST=127.0.0.1Without these settings, a client on a host with more than one network interface can receive replies from the same IOC over several addresses and print a warning for each one.
Start a soft IOC with one record
-
Create a directory for the IOC and enter it:
mkdir ../first-ioc cd ../first-ioc -
Create the file
first.dbwith one analog output record:record(ao, "tutorial:value") { field(VAL, "42") field(PINI, "YES") }PINIprocesses the record once at start, so the PV holds42with a valid time stamp. -
Start the IOC with the database:
softIocPVX -d first.dbThe IOC prints a banner and waits at its prompt:
INFO: PVXS QSRV2 is loaded, permitted, and ENABLED. Starting iocInit ############################################################################ ## EPICS R7.0.10-github.com/jeonghanlee/EPICS-env ## Rev. R7.0.10-dirty ## Rev. Date Git: 2025-12-15 17:11:22 -0600 ############################################################################ iocRun: All initialization complete 7.0.10 >The banner names the EPICS-env site version.
dirtymarks the base source tree thatmake patchchanged. The prompt7.0.10 >waits for IOC shell commands. The output above is from a terminal; when the IOC output goes to a pipe, the same lines can appear in a different order. -
At the IOC prompt, list the records:
dblThe IOC prints the name of its one record:
tutorial:value
Read and write the PV from a second shell
-
Open a second terminal in the working directory of the lesson and set up the same environment without the summary:
source <install_location>/1.4.0/debian-13/7.0.10/setEpicsEnv.bash disable export EPICS_CA_AUTO_ADDR_LIST=NO EPICS_CA_ADDR_LIST=127.0.0.1 EPICS_CAS_INTF_ADDR_LIST=127.0.0.1 export EPICS_PVA_AUTO_ADDR_LIST=NO EPICS_PVA_ADDR_LIST=127.0.0.1 EPICS_PVAS_INTF_ADDR_LIST=127.0.0.1 -
Read the PV over CA:
caget tutorial:valueThe output is:
tutorial:value 42 -
Read the PV over PVA:
pvget tutorial:valueThe output shows the time stamp and the value:
tutorial:value 2026-09-26 23:16:46.167 42PVA also carries the time stamp that
PINIset. Your output shows the time at which your IOC processed the record. -
Write the value
7over CA:caput tutorial:value 7The output shows the value before and after the write:
Old : tutorial:value 42 New : tutorial:value 7 -
Read the value again:
caget tutorial:valueThe output is:
tutorial:value 7 -
To stop the IOC, type
exitat the IOC prompt in the first terminal.
You built an installed tree from source, set up a shell on it, and exchanged a value with a running IOC over both protocols.
Pages to read after this lesson
- Build and install the environment repeats the build as a task with its verification.
- Set up a shell with the environment lists every variable the environment script sets.
- Run the verification gates checks the tree you built.
- Uninstall and clean removes the tree and the sources.
- The installed tree explains the layout.
Build pipeline stages
EPICS-env turns a checkout into an installed Experimental Physics and
Industrial Control System (EPICS) tree in six stages: init, patch,
conf, build, install, and symlinks. Each stage is a make target at
the repository top, and each one reads files that an earlier stage or the
configuration wrote.
Make targets by purpose
lists every target each stage runs, and
Build and install the environment runs
the stages in order.
What each stage reads and writes
| Stage | Reads | Writes |
|---|---|---|
init | The pins in configure/RELEASE and the generated configure/MODULESGEN.mk | epics-base-src and one <name>-src source tree per module, checked out at the pin |
patch | The .p0.patch files under patch/ | The cloned source trees |
conf | The configuration variables: install location, pins, and vendor library paths | Site files in epics-base-src/configure, RELEASE.local and CONFIG_SITE.local at the repository top, and site files under each module’s configure directory |
build | The patched source trees and the files conf wrote | EPICS base and every module in the installed tree |
install | The build products, scripts/setEpicsEnv.bash, scripts/resetEpicsEnv.bash, commonIocsh/iocsh/*.iocsh, and the EPICS-env commit | setEpicsEnv.bash, resetEpicsEnv.bash, .versions, and modules/commonIocsh/iocsh in the installed tree |
symlinks | The versioned directories in the installed tree | One unversioned link per module under modules |
init skips a source tree that already exists, so it never replaces a
checkout that patch has changed.
Upstream patch carry explains the patch files, and
Module set and dependencies explains the module site files
that conf writes.
Installed tree and relocation describes the tree that
build, install, and symlinks produce.
Why configuration precedes the build
The build target runs conf.base, build.base, conf.modules, and
build.modules, in that order. Every build therefore reads site files
written from the variables in effect for that make run.
The configuration decides where the build puts its output and how it links:
conf.basesetsINSTALL_LOCATIONfor EPICS base to thebasedirectory of the installed tree. It also selects linking with library search paths relative to$ORIGIN, the directory of the loaded file.conf.modulespoints every module at the installed EPICS base and sets each module’sINSTALL_LOCATIONto its versioned directory undermodules.- The module configuration targets write the install directory of each
dependency into the module’s
RELEASE.local, so a module compiles against the dependencies already installed.
An EPICS build installs its products into INSTALL_LOCATION as part of the
build. A module build therefore reads EPICS base, and every module it
depends on, from the installed tree rather than from a source tree. Without
the configuration, EPICS base and each module would install into their own
source trees.
Most configuration targets rewrite their files from the first line, so a
later make conf or make build replaces the earlier content. Some targets
change files in other ways:
conf.base.siteadds its two lines to the EPICS base fileconfigure/os/CONFIG_SITE.linux-x86_64.linux-x86_64only when each line is absent.conf.calc,conf.lua, andconf.StreamDeviceedit module files in place withsed, andconf.StreamDeviceandconf.pmacremove module files.- The
conf.gz.*targets append compression flags to files that other targets wrote.
On Ubuntu 26, each of the ten modules listed in
Source configuration targets
writes its C17 compiler flag during its own configuration. The Ubuntu 26
condition is defined before the automatic module rules are generated, so
conf.iocStats and the custom configuration targets use the same condition.
Serial execution of every target
configure/RULES_VARS declares .NOTPARALLEL. GNU Make then runs the
prerequisites of every target one at a time, in the order the target lists
them, even when you pass -j.
Several aggregates depend on that order:
patch.revertlists the patch targets in the exact reverse order ofpatch.github.checkrunscheck.module-depsafterconfand beforebuild.
Parallel compilation happens inside a single build. build.base runs the
EPICS base build with four parallel jobs; each module build runs make
without a -j option.
Module builds in dependency order
build.modules has one build.<module> prerequisite per module, followed by
install.modules. Each build.<module> target has the prerequisites that
the <module>_DEPS variable in configure/CONFIG_MODS_DEPS lists, such as
null.base build.sequencer build.sscan build.calc for asyn.
Make runs each build.<module> target once per make run. A module therefore
starts to build only after every module it depends on has built and
installed. The order among modules with no dependency relation between them
carries no meaning.
After the last module builds, install.modules runs make install in every
module source tree.
Module set and dependencies
describes the <module>_DEPS graph.
Install and link stages
The build stage alone leaves the installed tree without its top-level
files. install adds them:
install.baseruns the EPICS base install and copiesscripts/setEpicsEnv.bashandscripts/resetEpicsEnv.bashto the top of the installed tree with mode 0644, backing up existing files.install.modulesruns the install of every module.install.commoniocshcopies the common iocsh fragments tomodules/commonIocsh/iocsh.src_versionwrites the time it runs and the EPICS-env commit to.versionsand copies that file to the top of the installed tree.
symlinks creates an unversioned link, such as modules/asyn, for each
module. On Linux it then deletes every dangling link under modules.
setEpicsEnv.bash reaches the pvxs and pmac executables through these
links.
Continuous integration aggregates
Two aggregates run the pipeline for continuous integration (CI):
| Aggregate | Runs, in order |
|---|---|
github | init, patch, vars, conf, build, symlinks |
github.check | init, patch, vars, conf, check.module-deps, build, symlinks |
vars prints the configuration variables into the job log. Neither aggregate
runs install; a CI workflow that uses github.check runs make install
after it.
github.check places check.module-deps after patch and conf because
that audit reads the patched module sources and the RELEASE.local files
that conf writes. The installed-tree checks check.deps and check.env
need an installed tree, so neither aggregate runs them.
Build and install verification gates explains each
check, and
Supported platforms and CI
lists which workflow runs which aggregate.
Module set and dependencies
The module set is the list of Experimental Physics and Industrial Control
System (EPICS) modules that EPICS-env clones, configures, builds, and
installs beside EPICS base. The configuration files define it: configure/RELEASE
names each module and its pin, configure/CONFIG_MODS sets where each module
comes from, configure/CONFIG_MODS_TYPES declares each configuration type, and
configure/CONFIG_MODS_DEPS records its build dependencies and optional
configuration settings.
Module pins and dependencies lists the values
for every module, and
Add or bump a module changes them.
Module declaration by RELEASE triples
Each module has a module key, an upper-case name such as ASYN or SNCSEQ.
configure/RELEASE defines three variables per key:
| Variable | Meaning | Example for ASYN |
|---|---|---|
SRC_NAME_<MODULE_KEY> | Repository name; also the source directory name without -src | asyn |
SRC_TAG_<MODULE_KEY> | Tag or commit to check out | tags/R4-46 |
SRC_VER_<MODULE_KEY> | Version in the install directory name | 4.46.0 |
The set holds every key whose SRC_NAME_<MODULE_KEY> variable a file
defines; SRC_NAME_BASE names EPICS base and stays outside the set. A
SRC_NAME_<MODULE_KEY> value from the environment or the make command line
does not add a module. A RELEASE.local file can override a triple;
Configuration variables and override files
gives the read order.
From the triple, make derives three names for each module:
- The source tree
<name>-srcat the repository top, such asasyn-src.recsyncbuilds fromrecsync-src/client. - The install directory
modules/<name>-<version>in the installed tree, such asmodules/asyn-4.46.0. - The target suffix
<name>, as inbuild.asynandinstall.asyn.
Generated module variables in MODULESGEN.mk
configure/CONFIG_MODS derives module variables from the effective pins.
Actions include configure/MODULESGEN.mk, a generated file Git ignores.
Query-only invocations derive the same variables in memory without reading
or writing that cache. For each module key, the derivation supplies three variables:
| Variable | Value |
|---|---|
SRC_GITURL_<MODULE_KEY> | The value of SRC_URL_EPICSMODULES, expanded when make writes the file, followed by /<name>, such as https://github.com/epics-modules/asyn |
INSTALL_LOCATION_<MODULE_KEY> | $(INSTALL_LOCATION_MODS)/<name>-<version>; $(INSTALL_LOCATION_MODS)/seq-<version> for the sequencer |
SRC_PATH_<MODULE_KEY> | <name>-src |
An action invocation generates the file when it is absent, or when
configure/RELEASE or configure/CONFIG_SITE carries a later modification
time. The file also records the effective module triples used to generate it.
Make regenerates it when those values change, including after creating,
editing, or removing configure/RELEASE.local or ../RELEASE.local.
Make reads the regenerated file in the same run, so the install directories
follow the effective pins. An unchanged action invocation preserves the cache. Query-only invocations
use current settings even when the cache is absent or stale; they preserve
its bytes and modification time.
make show.genmk displays the existing cache, including stale values.
When the cache is absent, it prints the current generated configuration
without creating a file. It also displays other existing configure/*.mk files.
make reconf.modules explicitly removes the generated files under
configure/ and regenerates MODULESGEN.mk from the current settings.
Repository overrides in CONFIG_MODS
The generated repository address of every module points at
https://github.com/epics-modules. After it derives or reads the module variables,
configure/CONFIG_MODS replaces that address for the twelve modules hosted
elsewhere:
| Organization variable | Modules |
|---|---|
SRC_URL_BASE | pvxs |
SRC_URL_CHANNELFINDER | recsync |
SRC_URL_BRUNOSEIVAM | retools |
SRC_URL_PSI | StreamDevice |
SRC_URL_JEONGHANLEE | snmp, QPC, rgamv2 |
SRC_URL_MOTOR | motorMotorSim |
SRC_URL_PMAC | pmac |
SRC_URL_MD | pscdrv, linStat |
SRC_URL_BERKELEYLAB | feed-core |
A module outside epics-modules therefore needs both its triple in
configure/RELEASE and one override line in configure/CONFIG_MODS.
Build order from declared dependencies
configure/CONFIG_MODS_DEPS defines one <module>_DEPS variable per module.
Its value is null.base, an empty target that stands for EPICS base,
followed by one build.<module> entry for each module that must build
first:
asyn_DEPS:=null.base build.sequencer build.sscan build.calc
Make turns each <module>_DEPS value into the prerequisites of
build.<module>, which gives the dependency order of
the build stage.
The Depends on column of
Module repositories and pins
shows the same graph.
<module>_DEPS is also the declared dependency list that check.module-deps
compares with the references it finds in each module’s source tree.
Build and install verification gates
describes that comparison.
Configuration types auto and custom
configure/CONFIG_MODS_TYPES declares each module’s <module>_CONF_TYPE, with the
value auto or custom. The type decides where its conf.<module> target
comes from.
An auto module gets a generated conf.<module> target. That target writes
one line to the module’s configure/CONFIG_SITE.local: INSTALL_LOCATION
set to the module’s install directory. Three optional variables extend it:
| Variable | Effect | Module that sets it |
|---|---|---|
<module>_CONF_RELEASE_LINES | Writes the value to configure/RELEASE.local | iocStats |
<module>_CONF_SITE_LINES | Appends the value to configure/CONFIG_SITE.local | retools |
<module>_CONF_PLATFORM | Runs the target only when uname -s prints this value | MCoreUtils |
The auto modules are MCoreUtils, autosave, caPutLog, ether_ip,
iocStats, pcas, pscdrv, retools, and snmp. A custom module has a
hand-written conf.<module> target in configure/RULES_MODS_CONFIG, which
can write any site setting the module needs.
Make checks the declarations each time it reads the makefiles, before it runs any target:
- Every module in the set must declare
<module>_CONF_TYPE, and the value must beautoorcustom. - Every
automodule must have a source path, and exactly one install directory name in the module set must start with<module>-.
A failed check stops every make command, including
distclean.modulesgen, with a message such as
Missing foo_CONF_TYPE declaration. The message names the recovery for a
stale generated file: remove configure/MODULESGEN.mk with rm and run make
again.
Dependency paths through RELEASE.local
A module learns where EPICS base and its dependencies are installed from
RELEASE.local files that the configuration writes.
conf.release.modules writes two files at the repository top, one directory
above every module source tree:
RELEASE.localsetsEPICS_BASEto the installedbasedirectory and clearsSUPPORT.CONFIG_SITE.localsetsCHECK_RELEASE = NOand adds-Wl,--enable-new-dtagstoPROD_LDFLAGS.
A module whose configure/RELEASE reads $(TOP)/../RELEASE.local, and whose
configure/CONFIG_SITE reads $(TOP)/../CONFIG_SITE.local, picks up both
files; asyn and autosave read them this way.
Most custom targets for modules with dependencies write the install
directory of each dependency into the module’s own
configure/RELEASE.local. conf.asyn, for example,
writes SNCSEQ, SSCAN, and CALC, each set to the absolute path of a
versioned directory, such as /home/user/epics/1.4.0/debian-13/7.0.10/modules/seq-2.2.9.
Two targets write other files:
conf.motorMotorSim writes the module’s configure/RELEASE and
configure/CONFIG_SITE, and conf.pmac writes its configure/CONFIG_SITE
and configure/RELEASE.local.
The dependencies a custom target writes form a list separate from
<module>_DEPS. check.module-deps reads the written RELEASE.local as
evidence and reports where the two lists disagree.
These generated dependency paths name versioned directories, so a module links against the dependency version the module set pins.
QPC inherits ASYN=$(EPICS_BASE)/../modules/asyn from its own
configure/RELEASE; conf.QPC writes only CONFIG_SITE.local.
Its active digitelQpcApp tree installs data and IOC fragments and declares
no library or executable. The installed gamma-pctrl.iocsh fragment uses
the consuming IOC’s ASYN macro to locate asynRecord.db at startup.
That runtime macro is separate from QPC’s inherited, unversioned build setting.
Sequencer installed as seq
The sequencer module carries several names:
| Use | Name |
|---|---|
| Module key | SNCSEQ |
| Repository and source tree | sequencer, sequencer-src |
| Build and install targets | build.sequencer, install.sequencer |
| Configuration target | conf.sncseq |
| Install directory and unversioned link | modules/seq-<version>, modules/seq |
configure/CONFIG_VARS maps sequencer to seq for the install directory
and the link. The mapping ignores a SRC_NAME_SNCSEQ value from the
environment or the command line. The dependency audit treats SNCSEQ,
seq, and pv as references to sequencer.
MCoreUtils on Linux only
configure/RELEASE defines the MCOREUTILS triple only when uname -s
prints Linux. On any other system, MCoreUtils is absent from the module
set, so make generates no clone, build, or install target for it. Its
auto configuration target also carries Linux as its platform.
Installed tree and relocation
The installed tree is the directory that holds one built Experimental Physics and Industrial Control System (EPICS) base, every module in the module set, and the files that set up a shell for them. Its binaries find their shared libraries by position relative to themselves, so the whole directory works after you move or copy it. Choose the install location and release sets where the tree goes, and Set up a shell with the environment uses it.
Directory layout by release, system, and base version
EPICS-env installs each tree at this path:
$(INSTALL_LOCATION)/$(ENV_RELEASE_VERS)/<os_id>-<os_version>/$(SRC_VER_BASE)
<os_id> and <os_version> are the ID and VERSION_ID values from
/etc/os-release. The make variable INSTALL_LOCATION_EPICS holds the whole
path, such as /home/user/epics/1.4.0/debian-13/7.0.10, so trees for several
releases, operating systems, and EPICS base versions can share one
INSTALL_LOCATION.
Install location and release
lists the variables.
One tree has this layout, shown for two of its modules:
<install_location_epics>/
|-- setEpicsEnv.bash
|-- resetEpicsEnv.bash
|-- .versions
|-- base/
| |-- bin/linux-x86_64/
| `-- lib/linux-x86_64/
|-- modules/
| |-- asyn-4.46.0/
| |-- asyn -> ./asyn-4.46.0
| |-- seq-2.2.9/
| |-- seq -> ./seq-2.2.9
| `-- commonIocsh/
| `-- iocsh/
`-- vendor/
`-- lib/
Versioned directories and unversioned links
Each module installs into modules/<name>-<version>, where <version> is the
SRC_VER_<MODULE_KEY> value of the module, such as asyn-4.46.0 or
calc-4217e83. The sequencer installs as seq-<version>.
make symlinks adds an unversioned link for each module, such as
modules/asyn, that points to the versioned directory by a relative path.
The link gives a path that stays the same when a pin changes.
The build itself uses only versioned directories. Every module
RELEASE.local and every library search path in a binary names a versioned
directory, so removing or replacing the links changes no binary.
setEpicsEnv.bash uses the pvxs and pmac links to put their executables
on PATH.
Environment script and version record
install.base copies scripts/setEpicsEnv.bash and
scripts/resetEpicsEnv.bash to the top of the tree with mode 0644. It backs
up files it replaces. You source setup in a Bash shell. The script finds the tree from its own location,
and follows a symbolic link to the script when there is one. It then sets
the shell environment in four ways:
- It sets
EPICS_PATHto the tree,EPICS_BASEto itsbasedirectory, andEPICS_MODULESto itsmodulesdirectory. - It sets
EPICS_HOST_ARCHfrom theEpicsHostArch.plscript of EPICS base, whichperlruns, or else frombase/startup/EpicsHostArch, whichshruns. Whenperlor all three scripts are absent, it uses its own fallback architecture argument. Its interface is[<fallback_arch>] [disable];disablecontrols only the summary. - It adds
base/bin/<arch>,modules/pvxs/bin/<arch>, andmodules/pmac/bin/<arch>to the front ofPATH. - It adds
base/lib/<arch>to the front ofLD_LIBRARY_PATH.
When EPICS_BASE is already set, the script first removes the entries of the
earlier tree from PATH and LD_LIBRARY_PATH after resolving the selected
tree and architecture. Complete-field comparison preserves unrelated entries
and empty fields; repeated setup adds managed directories once. Directly
sourcing another tree’s setup selects that tree. Resolution failure preserves
the existing managed environment.
Reset removes the known executable and base library entries and unsets
EPICS_PATH, EPICS_BASE, EPICS_MODULES, EPICS_HOST_ARCH, and legacy
EPICS_EXTENSIONS, including when base is absent. Both scripts preserve
independently configured CA settings and operate with Bash nounset enabled.
src_version writes .versions at the top of the tree. It records when the
install ran and which EPICS-env commit it used. The .versions file of one
tree reads:
Timestamps : 20260926-225114/YYYYMMDD-HHMMSS
git version :4601e4049ac3f846806ec59a7cf462f4f9459393
Common iocsh fragment directory
install.commoniocsh copies commonIocsh/iocsh/*.iocsh from the repository
to modules/commonIocsh/iocsh. That directory has no version in its name and
no unversioned link. An input/output controller (IOC) sets IOCSH_TOP to
its parent, modules/commonIocsh, and loads each fragment as
$(IOCSH_TOP)/iocsh/<fragment>.iocsh.
Common iocsh fragments explains the fragments,
and Load common iocsh fragments in an IOC
uses them.
Vendor library directory
vendor/ holds third-party libraries that two modules link: uldaq for
measComp and open62541 for opcua. No EPICS-env make target creates it.
The continuous integration (CI) workflows and tools/prep-vendors.bash
install both libraries there, and point VENDOR_ULDAQ_PATH and
OPEN62541_PATH at it in configure/RELEASE.local. With the default value
/usr/local of both variables, the modules link the libraries from that
system location instead.
Vendor library locations
lists the two variables.
vendor/ lies inside the tree, so make uninstall removes it with the rest
of the tree.
Library search paths relative to ORIGIN
A shared library or executable in the Executable and Linkable Format (ELF)
carries a list of directories that the dynamic loader searches for the
libraries it needs. EPICS-env makes every entry for a library inside the tree
relative to $ORIGIN, which the loader replaces with the directory of the
file being loaded.
Two EPICS base build variables produce the relative entries:
LINKER_USE_RPATHisORIGIN.conf.base.sitewrites it to the EPICS base site file, and every build receives it on the make command line.LINKER_ORIGIN_ROOTnames the root of the relocatable tree. Every EPICS base and module build receives it on the make command line, set toINSTALL_LOCATION_EPICS, the top of the tree.
EPICS base records a library directory under that root as a path relative to
$ORIGIN, and a directory outside the root as an absolute path.
The linker option -Wl,--enable-new-dtags stores the list as a RUNPATH
entry, the run-time library search path, rather than an RPATH entry.
check.deps fails on any RPATH entry, and the configuration passes the
option to every kind of link:
| Where the configuration sets it | Variable | Links |
|---|---|---|
EPICS base configure/CONFIG_SITE.local | PROD_LDFLAGS_DEFAULT | Executables of EPICS base and of every module, because EPICS base installs this file and its configure/CONFIG_SITE reads it |
EPICS base configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 | SHRLIB_LDFLAGS, LOADABLE_SHRLIB_LDFLAGS | Shared libraries of EPICS base and of every module, because EPICS base installs this file |
CONFIG_SITE.local at the repository top | PROD_LDFLAGS | Executables of the modules that read this file |
From the top of an installed tree, this command prints the libraries that the asyn library needs and its search path:
readelf -d modules/asyn-4.46.0/lib/linux-x86_64/libasyn.so | grep -E 'NEEDED|RUNPATH'
The output is:
0x0000000000000001 (NEEDED) Shared library: [libdbCore.so.3.25.0]
0x0000000000000001 (NEEDED) Shared library: [libCom.so.3.25.0]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN/../../../../base/lib/linux-x86_64:$ORIGIN/.]
The first entry leads from modules/asyn-4.46.0/lib/linux-x86_64 up four
levels to the top of the tree and down to the EPICS base libraries. No entry
names the directory the tree was built in.
Why the tree can be moved
Two properties make the tree independent of the directory it was built in:
- Every library search path to a library inside the tree is relative to
$ORIGIN, so the loader finds EPICS base, module, and vendor libraries at the same relative positions after a move. setEpicsEnv.bashcomputes every path it sets from its own location.
check.deps guards the first property. It fails when an installed binary
carries an RPATH entry or an absolute path outside the system library
directories. It also fails on a shared library that needs a library of the
tree but has no $ORIGIN entry.
Build and install verification gates describes it.
A search path can also name a system library directory, such as
/usr/lib/x86_64-linux-gnu in the pmac and pyDevSup libraries. Such a
path does not depend on where the tree lies.
Relocation covers the loader and the environment script, not every text file. These installed files keep the absolute path of the original tree:
- The EPICS base
configure/CONFIG_SITE.local. - The
configure/RELEASE.localfile that 16 of the installed modules carry, except the one ofiocStats, which sets onlyMAKE_TEST_IOC_APP=NO. - The
S99caRepeater,S99logServer, andcaRepeater.servicefiles inbase/bin/linux-x86_64. - The
epics-base.pcandepics-base-linux-x86_64.pcfiles inbase/lib/pkgconfig. - The
libuldaq.laandpkgconfig/open62541.pcfiles undervendor/lib.
The tree path names one operating system and version, and the tree links the system libraries of that system. A moved tree therefore belongs on a host that runs the same operating system and version.
make uninstall removes the tree at the path that the current configuration
computes. Uninstall and clean covers
removal.
Build and install verification gates
EPICS-env checks the Experimental Physics and Industrial Control System (EPICS) build at two points: before compilation, it compares module dependencies against the sources, and after installation, it inspects the installed tree. Each check is a make target in two forms. The report form prints findings and exits 0 on them, and the strict form exits with a failure code on a finding. Both forms exit with a failure code on a usage error or a missing tool. The strict form is the gate. Run the verification gates runs them, and Verification and inspection targets lists the targets.
Gates and the stage each one guards
| Gate | Report form | Stage | What it reads | Script |
|---|---|---|---|---|
check.module-deps | audit.module-deps | After conf, before build | Module source trees | tools/audit_module_deps.bash |
check.deps | audit.deps | After install | Installed binaries and shared libraries | tools/check_deps.bash |
check.env | audit.env | After install | Installed setEpicsEnv.bash | tools/check_env.bash |
check.module-deps is static: it reads text and compiles nothing.
check.deps and check.env read the installed tree, so they belong after
make install. Neither aggregate github nor github.check runs them.
Static module dependency audit
check.module-deps compares, for each module, the dependencies the module
declares with the dependencies its source tree shows. The declared list is
the <module>_DEPS variable in configure/CONFIG_MODS_DEPS.
Module set and dependencies
describes that variable.
The audit reads these files in each module source tree and records every reference to another module:
| Source | Reference | Strength |
|---|---|---|
configure/RELEASE.local, which conf writes | A macro that names a module, such as ASYN | Required |
Makefile files | A database definition (.dbd) file in a DBD line | Required |
Makefile files | A library in a _LIBS line | Probable |
| Database definition, database, and protocol files | A referenced .dbd, database, or .proto file | Required |
| Database files | A record type | Probable |
Startup scripts (st.cmd, *.cmd, *.iocsh) | A file that dbLoadRecords, dbLoadTemplate, or dbLoadDatabase loads | Required |
| C and C++ sources and headers | An included header | Probable |
The location of a file can weaken a reference to optional. A reference under
a test, example, demonstration, or iocBoot directory is optional, and so is
one inside a conditional block of a Makefile. Documentation directories
are ignored. Build output directories are ignored too, except under a test
directory, where their files count as optional. A reference to a
known external library, such as ftdi, appears as external and never
fails. References under os/Linux, os/posix, and
os/default stay required only when the PLATFORM variable is Linux, the
default on a Linux host. Only required references can produce a failing
finding; probable and optional references appear in the report.
The audit reports three kinds of finding:
| Finding | Meaning | Fails the strict form |
|---|---|---|
undeclared-observed | A required reference names a module that <module>_DEPS does not list | Yes |
unknown | A required reference matches no module, EPICS base library, or known external library | Yes |
declared-unobserved | <module>_DEPS lists a module that no required reference names | No |
The audit maps several spellings to one module, such as SNCSEQ, seq, and
pv to sequencer. configure/CONFIG_MODS_AUDIT holds the spellings, the
EPICS base libraries and record types, and the external libraries such as
ssl and z.
Because the audit reads patched sources and the RELEASE.local files that
conf writes, github.check runs it after patch and conf. The
feed-core and QPC patches remove source references to modules those
builds do not use; the strict audit passes for both modules with or without
those patches.
Installed runpath scan
check.deps inspects the Executable and Linkable Format (ELF) files of the
installed tree with readelf -d. It scans the executables under
base/bin/linux-x86_64 and modules/*/bin/linux-x86_64, and the shared
libraries under base/lib/linux-x86_64, each module’s lib/linux-x86_64,
and vendor/lib.
Executable paths are resolved to canonical paths before analysis. A versioned module directory and its unversioned link therefore contribute each executable only once to the scan and its counts.
It counts three defects:
- An
RPATHentry in any scanned file. ARUNPATHentry, the run-time library search path, belongs there instead. - An absolute directory in a library search path, other than a system
library directory such as
/usr/libor/usr/lib/x86_64-linux-gnu. - A shared library whose search path lacks
$ORIGINbut that needs a library found in the tree. The scan calls this a lost runpath.
A system library directory in a search path prints a note and is not a defect. Installed tree and relocation explains why these defects break relocation.
An empty installed-tree path, a path that is not a directory, or a failed
make lookup of INSTALL_LOCATION_EPICS exits 2 before scanning. The diagnostic
directs the caller to set INSTALL_LOCATION_EPICS or pass a valid
<installed_tree>. These input errors also fail with --report-only.
Run the check after make install; an existing directory alone does not prove
that every required component has been installed.
Environment script library path check
check.env sources the installed setEpicsEnv.bash in a child Bash shell
started with an empty environment and a PATH of /usr/bin:/bin. It then
reads the LD_LIBRARY_PATH that the script produced. The empty environment
keeps the caller’s own LD_LIBRARY_PATH, PATH, and EPICS_* variables out
of the result.
The check reports each LD_LIBRARY_PATH entry that contains a pvxs/bundle
path component. The build links the system libevent library, so that bundle
directory never exists, and the dynamic loader skips a missing directory
without an error. The check compares normalized entries, so doubled slashes
and . segments do not hide such an entry.
Report and strict forms and their exit codes
| Target | Exit 0 | Exit 1 | Exit 2 | Exit 3 |
|---|---|---|---|---|
audit.module-deps | Audit ran | Invalid argument or no matching module | Not used | Not used |
check.module-deps | No failing finding | Invalid argument or no matching module | One or more failing findings | Not used |
audit.deps | Scan ran | Invalid option | Invalid installed-tree input, unresolved executable path, or readelf not found | Not used |
check.deps | No defect | Invalid option | A defect, invalid installed-tree input, unresolved executable path, or readelf not found | Not used |
audit.env | Check ran or skipped | Invalid option | Not used | Not used |
check.env | No finding | Invalid option | One or more findings | No inspectable environment |
check.env treats three states as no inspectable environment: no installed
setEpicsEnv.bash, an empty EPICS_MODULES or EPICS_HOST_ARCH after the
script runs, and an empty LD_LIBRARY_PATH. audit.env prints SKIP for
the same states and exits 0.
When a script fails under a make target, make reports the script’s code in
its error message, such as Error 3, and exits 2 itself.
Where continuous integration runs each gate
Every continuous integration (CI) workflow for an operating system ends with
make exist, make check.env, and make check.deps, after make install.
The workflows for Debian 12, Debian 13, and Rocky Linux 8 build through
make github.check, so they also run check.module-deps before the build.
The workflows for Rocky Linux 10, Ubuntu 24.04, and Ubuntu 26.04 run the
stages one by one, with make check.module-deps immediately before
make build, after patching and configuration. An audit failure stops
the installation step before compilation.
Supported platforms and CI
lists the workflows.
Upstream patch carry
EPICS-env builds Experimental Physics and Industrial Control System (EPICS)
base and each module from a pinned upstream tag or commit. When a pinned
source needs a fix that the pin does not contain, EPICS-env carries the fix
as a patch file under patch/ and applies it to the cloned source during
the patch stage. The pin in configure/RELEASE stays unchanged; a carried
patch and a version bump are separate changes.
Carry an upstream fix as a patch adds a patch,
Verify a fix against an installed tree
tests one, and
Upstream patch targets
lists the targets.
Patch file families and names
The file name decides which target applies a patch and to which source tree:
| Family | File name | Apply target | Source tree |
|---|---|---|---|
| EPICS base carry, merged pull request | <base_version>-pr<NNNN>-<slug>.p0.patch | patch.base.pr.apply | epics-base-src |
| EPICS base carry, direct commit | <base_version>-<NN>-<sha7>-<slug>.p0.patch | patch.base.pr.apply | epics-base-src |
| pvxs carry | <pvxs_version>-<NN>-<sha7>-<slug>.p0.patch | patch.pvxs.commit.apply | pvxs-src |
| EPICS base site patch | <base_version>.base.p0.patch | patch.base.apply | epics-base-src |
| Fixed module patch | <module>-<slug>.p0.patch | patch.<name>.apply | One module |
The placeholders in the names mean:
<base_version>and<pvxs_version>are the values ofSRC_VER_BASEandSRC_VER_PVXS, such as7.0.10and1.5.2.<NNNN>is the number of the upstream pull request (PR) that merged the fix, written with four digits.<NN>is a two-digit sequence number, and<sha7>is the first seven characters of the upstream commit.<slug>is a short description of the fix.
For the pins in configure/RELEASE, patch/ holds 18 EPICS base carry
patches for 7.0.10 and 12 pvxs carry patches for 1.5.2. No
7.0.10.base.p0.patch exists, so patch.base.apply does nothing.
Version-anchored names that drop on a bump
The carry targets select their files with a pattern that starts with the pinned version:
| Target | Pattern |
|---|---|
patch.base.pr.apply | patch/$(SRC_VER_BASE)-*.p0.patch |
patch.pvxs.commit.apply | patch/$(SRC_VER_PVXS)-*.p0.patch |
When the pin of EPICS base or pvxs moves to another version, its pattern matches no file, and the whole carry set for that source stops applying. Every carried fix then needs a review against the release that the pin names, and a carry patch never reaches a version it was not written for.
The hyphen after the version keeps the carry pattern apart from the site
patch <base_version>.base.p0.patch. The pvxs pattern 1.5.2-* also never
matches the file pvxs-1.3.1.p0.patch.
Sorted apply and exact reverse revert
A carry target applies its files in the sorted order of their names, and the matching revert target removes them in the exact reverse order. Patches that touch the same file then apply and revert against the source state each one expects.
Revert targets classify each whole patch with noninteractive dry-runs. They skip confirmed unapplied patches and reverse confirmed applied patches. Conflicts, partial application, required-input absence, and unresolved states stop the invocation; earlier completed reversals remain in effect.
Later carry patches can depend on context from earlier patches. Classification can prepare that context in a private copy of real source files, preserving the original tree. For supported single-hunk static C function patches, it also checks the named function to distinguish similar code in other functions. Private hunk search positions follow that function when independent source edits move it. Patch contents must match inside the function; an absent or duplicate function leaves the state unresolved.
Sorting compares names as text. Within the EPICS base set, the two-digit
commit form sorts before the pull request form, because a digit sorts before
p. The EPICS base set for 7.0.10 therefore applies
7.0.10-01-b2d2758-putnotify-type-check.p0.patch first, followed by the pull
request patches in ascending number.
Each file goes through patch -d <source_tree> --ignore-whitespace -p0.
The carry targets stop at the first file that fails, so a later success cannot
hide an earlier failure. The pvxs targets also pass --no-backup-if-mismatch,
so a patch that applies at an offset leaves no .orig backup file in the
source tree.
All revert targets suppress mismatch backups and preserve existing backups.
The patch aggregate applies every family in a fixed order, and
patch.revert lists the same targets in the exact reverse order:
patch.base.applypatch.base.pr.applypatch.mca.applypatch.measComp.applypatch.measComp.tc32.applypatch.opcua.applypatch.opcua.export.applypatch.feed-core.applypatch.QPC.applypatch.pvxs.commit.applypatch.StreamDevice.apply
Fixed per-module patches
A fixed patch has no version in its name, and one dedicated target pair
applies and reverts it. It keeps applying after the module pin changes,
until it fails to apply to the changed source and make patch stops.
| File | Target | Change |
|---|---|---|
measComp-CONFIG_MEASCOMP.p0.patch | patch.measComp | Installs cfg/CONFIG_MEASCOMP, so a module that names MEASCOMP inherits ULDAQ_DIR |
measComp-tc32-chan-count.p0.patch | patch.measComp.tc32 | Halves the reported TC-32 thermocouple channel count when no expansion unit is present |
opcua-CONFIG_OPCUA.p0.patch | patch.opcua | Derives the open62541 library and include directories from OPEN62541 in the installed CONFIG_OPCUA |
opcua-anon-ns-export.p0.patch | patch.opcua.export | Moves exported registration blocks out of unnamed namespaces, which the GNU Compiler Collection (GCC) 15 needs to link them |
feed-core-libonly.p0.patch | patch.feed-core | Trims the build to the library and drops the busy, asyn, and autosave references of the unused bundled application |
QPC-dataonly.p0.patch | patch.QPC | Removes module references from the unbuilt example application Makefile |
StreamDevice-no-vxi11.p0.patch | patch.StreamDevice | Removes the vxi11 driver registration, which asyn builds only with DRV_VXI11=YES |
mca-libnet.p0.patch | patch.mca | Acts only on macOS; the target does nothing on Linux |
The Target column names the prefix of the .apply, .revert, and .make
targets.
The feed-core and QPC patches remove module references from Makefile
files that the module dependency audit check.module-deps reads. On the
current pins, the strict
audit passes in all four combinations: both patches applied, only feed-core
applied, only QPC applied, and neither applied. These patches define the
selected build content; passing the audit does not require them.
Build and install verification gates
describes that audit.
Patch file format
Every patch file is a unified diff with no path prefix, the form that
git diff --no-prefix writes. Paths in the file start at the top of the
source tree, such as modules/database/src/std/rec/mbbiRecord.c for EPICS
base, and the -p0 option applies them from there. The .p0.patch suffix
marks this format.
The .make targets write a patch from the current changes in a source tree:
patch.<name>.makewrites the fixed patch of one module from the current changes in its source tree, limited for most modules to the files that patch covers.patch.base.makewrites every change inepics-base-srcto<base_version>.base.p0.patch.
No make target writes the EPICS base or pvxs carry files.
Inactive patch files
Some files under patch/ match no active target on a Linux build of the
pinned versions:
| File | Why it does not apply |
|---|---|
3.15.5.base.p0.patch, 7.0.5.base.p0.patch, 7.0.7.base.p0.patch | Their version differs from SRC_VER_BASE |
pvxs-1.3.1.p0.patch | No active target names it, and the pvxs carry pattern does not match it |
mca-libnet.p0.patch | Its targets act only on macOS |
Common iocsh fragments
The common iocsh fragments are shell script files that set up site services in an Experimental Physics and Industrial Control System (EPICS) input/output controller (IOC). Each fragment takes its settings as macros, so every IOC loads the same file with its own values. Load common iocsh fragments in an IOC uses them, Common iocsh fragment macros lists every macro, and Run the fragment verification suite checks them against running IOCs.
Fragment files and their services
The repository holds the fragments in commonIocsh/iocsh. Each service has
one fragment, except linStat and serial setup, which have more than one:
| Fragment | Service | Module the IOC links |
|---|---|---|
iocLog.iocsh | Sends the IOC error log to a log server | EPICS base |
caPutLog.iocsh | Sends a record of each Channel Access (CA) put to a log server | caPutLog |
autosave.iocsh | Saves record values and settings, and restores them at the next start | autosave |
reccaster.iocsh | Publishes the IOC’s record names to a recceiver service | recsync |
iocStatsAdmin.iocsh | Loads the IOC status records of the iocStats module | iocStats |
linStat.iocsh | Loads Linux host and process statistics through the four linStat*.iocsh sub-fragments | linStat |
serial.iocsh | Loads a serial configuration file that the IOC owns | none |
setSerialParams.iocsh | Sets the baud rate, data bits, stop bits, and parity of one asyn serial port | asyn |
Installed location and the IOCSH_TOP macro
make install runs install.commoniocsh, which copies every
commonIocsh/iocsh/*.iocsh file to modules/commonIocsh/iocsh in the
installed tree. The directory name carries no version, so one installed tree
holds one fragment set.
Installed tree and relocation shows where the directory
sits in the tree.
An IOC names modules/commonIocsh, the directory that holds iocsh, in the
macro IOCSH_TOP and loads a fragment with iocshLoad:
iocshLoad("$(IOCSH_TOP)/iocsh/<fragment>.iocsh", "<macros>")
<fragment> is the file name without .iocsh, and <macros> is a
comma-separated list of NAME=value pairs. Neither the IOC build nor
envPaths sets IOCSH_TOP; the startup script sets it with epicsEnvSet,
or the IOC inherits it from the process environment.
linStat.iocsh relies on this form, because it loads its sub-fragments
from $(IOCSH_TOP)/iocsh:
iocshLoad("$(IOCSH_TOP)/iocsh/linStatHost.iocsh", "IOC=$(IOC)")
iocshLoad("$(IOCSH_TOP)/iocsh/linStatProc.iocsh", "IOC=$(IOC)")
How a fragment receives its values
A fragment reads three kinds of values:
- Module top macros.
autosave.iocsh,reccaster.iocsh,iocStatsAdmin.iocsh, and the linStat fragments read the install directory of their module fromAUTOSAVE,RECCASTER,devIocStats, andLINSTAT. The IOC build writesiocBoot/<ioc_name>/envPathswith oneepicsEnvSetline for eachconfigure/RELEASEmacro that names an existing directory. The startup script reads it with< envPaths, so each module macro inconfigure/RELEASEmust use exactly these names. - The
IOCmacro. Most fragments build record names and paths fromIOC: record prefixes such as$(IOC):, the autosave directory$(AS_TOP)/$(IOC), and the log prefixproc=$(IOC).envPathssetsIOCto the name of theiocBoot/<ioc_name>subdirectory, and the startup script passes it on asIOC=$(IOC). - Service settings. Every other macro has a default written as
$(NAME=default)in the fragment, or is required and has none.
linStat.iocsh and serial.iocsh have optional parts that an empty macro
turns on. The macros NICENABLE, FSENABLE, and SERIAL_ENABLE default to
#--, which turns the line that starts with them into an iocsh comment.
Passing the macro with an empty value, such as NICENABLE=, leaves the line
as a command:
$(NICENABLE=#--) iocshLoad("$(IOCSH_TOP)/iocsh/linStatNIC.iocsh", "IOC=$(IOC),NIC=$(NIC=)")
Load order in the startup script
The fragments depend on the IOC state at the point where the startup script loads them:
- The fragments load after
dbLoadDatabaseand the application’sregisterRecordDeviceDrivercall. Most of them use commands and variables that the module database definition (DBD) files register, such asasynSetOptionandreccastTimeout. - Every fragment loads before
iocInit.dbLoadRecordsmust run beforeiocInit, autosave restores its first pass duringiocInit, andafterIocRunningaccepts commands only beforeiocInit. iocLog.iocshloads before any device setup. ItsiocLogInitcall registers the error log listener, and a message printed before that call does not reach the log server.serial.iocshloads afterdrvAsynSerialPortConfigurecreates each port that the serial configuration file names.caPutLog.iocshneeds an access security configuration file (ACF) that marks write access withTRAPWRITE. caPutLog receives puts only from that trap, and the IOC loads its own file withasSetFilenamebeforeiocInit. The fragment registerscaPutLogInitwithafterIocRunning, so the logger starts afteriocInitcompletes.autosave.iocshcreates its directories with the iocshsystemcommand, which exists only when the IOC includessystem.dbdfrom EPICS base.
This ACF gives every client read access and traps every write:
ASG(DEFAULT) {
RULE(1, READ)
RULE(1, WRITE, TRAPWRITE)
}
linStat entry fragment and sub-fragments
linStat.iocsh is the entry point for the linStat service. It always loads
linStatHost.iocsh and linStatProc.iocsh, and loads one
linStatNIC.iocsh and one linStatFS.iocsh when their enable macros are
empty. Each sub-fragment loads one database of the linStat module:
| Sub-fragment | Statistics | Record names |
|---|---|---|
linStatHost.iocsh | Host memory, processor, uptime, hardware sensors, and interrupts | $(IOC):SYS_*, $(IOC):MEM_*, $(IOC):NET:HOST:*, and others under $(IOC): |
linStatProc.iocsh | Memory, file descriptors, threads, CA clients, and records of this IOC process | Under $(IOC): |
linStatNIC.iocsh | One network interface | $(IOC):NET:$(NIC):* |
linStatFS.iocsh | One mounted filesystem | $(IOC):$(FSID):* |
To monitor more than one network interface or filesystem, the startup script
loads linStatNIC.iocsh or linStatFS.iocsh once more for each one, with a
distinct NIC or FSID.
iocStatsAdmin and linStat record name collisions
The database that iocStatsAdmin.iocsh loads, iocAdminSoft.db, shares
record names under $(IOC): with both linStat host and process databases.
linStatHost.db, which linStatHost.iocsh loads, defines these names with a
different record type:
$(IOC):CPU_CNT$(IOC):MEM_FREE$(IOC):MEM_MAX$(IOC):MEM_USED$(IOC):SYS_CPU_LOAD
linStatProc.db, which linStatProc.iocsh loads, defines more names with a
different record type, such as $(IOC):FD_CNT, $(IOC):IOC_CPU_LOAD, and
$(IOC):SYSRESET.
When both services load with the same IOC value and iocStatsAdmin.iocsh
loads first, dbLoadRecords reports already exists for each name with a
different record type. It then reports Failed to load for linStatHost.db
and linStatProc.db. When linStat.iocsh loads first, the IOC reports the
same already exists errors and then crashes while it loads
iocAdminSoft.db. A name
that both databases define with the same record type merges into one record
without an error, such as $(IOC):HOSTNAME, $(IOC):KERNEL_VERS, and
$(IOC):TOD of linStatHost.db. An IOC therefore loads either linStat or
iocStatsAdmin under one prefix. The integrated check of the verification
suite loads linStat and leaves out iocStatsAdmin.
IOC can hold at most 21 characters when iocStatsAdmin.iocsh loads. The
fragment checks no length; the longest record name of iocAdminSoft.db adds
39 characters to $(IOC), and an EPICS record name holds at most 60
characters. With a longer IOC, dbLoadRecords reports Failed to load for
iocAdminSoft.db.
Choose the install location and release
Set the install location before running an action in an EPICS-env clone.
Actions try to create it; query-only invocations inspect settings without
creating directories or generated files. The default is ${HOME}/epics.
Prerequisites
- A clone of EPICS-env. Run every command from the repository top.
- An absolute directory path that your user can create or write to.
python3onPATH;makestops while reading its configuration when it is missing.
-
In
configure/CONFIG_SITE.local, set the install location:echo "INSTALL_LOCATION=<install_location>" > configure/CONFIG_SITE.local<install_location>is the absolute path of the directory that holds every installed tree, such as/opt/epics.The file is untracked, so the setting survives a
git pull. ACONFIG_SITE.localin the directory that contains the repository applies to every clone in that directory;configure/CONFIG_SITE.localis read after it and wins. -
Optional: to install under a release number other than the default
1.4.0, addENV_RELEASE_VERSto the same file:echo "ENV_RELEASE_VERS=<release>" >> configure/CONFIG_SITE.local<release>names the first directory level below<install_location>. Trees with different release numbers sit side by side under<install_location>. -
To see where the installed tree goes, print its path:
make print-INSTALL_LOCATION_EPICSWith the default release, the output is:
<install_location>/1.4.0/debian-13/7.0.10With
<release>set to1.4.0-site, the output is:<install_location>/1.4.0-site/debian-13/7.0.10The path is
<install_location>/<release>/<os_id>-<os_version>/<base_version>.<os_id>and<os_version>are theIDandVERSION_IDvalues from/etc/os-release, and<base_version>is the version of Experimental Physics and Industrial Control System (EPICS) base pinned inconfigure/RELEASE. The output above is from a Debian 13 host. -
To check whether the build uses
sudo, printSUDO_INFO:make print-SUDO_INFOFor a location your user can create, the output is:
0A query reports
0when<install_location>is an existing directory whose parent path you can traverse, even if you cannot access the final directory. For an absent path, it checks search and create access at the nearest existing ancestor. It reports1for a non-directory or blocked path. This inspection cannot predict quota, read-only mount, or concurrent changes.Actions run the actual directory-creation probe. When that probe returns
1, make runs the module build and install steps, the module links, and thecommonIocshinstall throughsudo. The EPICS base build and install and the.versionsinstall never usesudo, so a complete install needs a<install_location>that your user can write. To use a system directory such as/opt/epics, create it and give your user ownership of it before you build. A query result of0does not guarantee permission to install into an existing directory.
Verification
-
Print the value in effect and where it comes from:
make PRINT.INSTALL_LOCATIONThe output names your path and the origin
file:INSTALL_LOCATION = <install_location> INSTALL_LOCATION's origin is file
Configuration variables and override files lists the defaults and the read order of the override files. To build into the chosen location, see Build and install the environment.
Build and install the environment
Build the vendor libraries, then run the EPICS-env target sequence to clone, patch, configure, build, and install Experimental Physics and Industrial Control System (EPICS) base and every module into the installed tree.
Prerequisites
- The install location is set; see Choose the install location and release.
- The host packages that EPICS base and the modules need are installed. The continuous integration (CI) workflows install them with https://github.com/jeonghanlee/pkg_automation; see Supported platforms and CI.
python3is onPATH;makestops while reading its configuration when it is missing.- The host can clone from GitHub.
- Every command runs from the top of the EPICS-env clone, in one shell, so the
VENDOR_PATHvariable of step 1 stays set.
When the uldaq and open62541 libraries are installed under /usr/local, the
default of VENDOR_ULDAQ_PATH and OPEN62541_PATH, skip steps 1 through 4.
-
To place the vendor libraries inside the installed tree, set a shell variable to its
vendordirectory:VENDOR_PATH="$(make print-INSTALL_LOCATION_EPICS)/vendor" -
In
configure/RELEASE.local, point themeasCompandopcuamodules at that directory:echo "VENDOR_ULDAQ_PATH=${VENDOR_PATH}" > configure/RELEASE.local echo 'OPEN62541_PATH=\$$\$$\(\_OPEN62541_CONFIG_OPCUA\)/../../../vendor' >> configure/RELEASE.localconf.measCompwritesVENDOR_ULDAQ_PATHinto themeasCompconfiguration as the uldaq library and header directories. The escapedOPEN62541_PATHvalue reaches the installedopcuaconfiguration filecfg/CONFIG_OPCUAas$(_OPEN62541_CONFIG_OPCUA)/../../../vendor, a path relative to that file’s own directory, so it keeps pointing at thevendordirectory of the same installed tree. -
Build the uldaq library into the
vendordirectory:git clone https://github.com/jeonghanlee/uldaq-env ../uldaq-env echo "INSTALL_LOCATION=${VENDOR_PATH}" > ../uldaq-env/configure/CONFIG_SITE.local make -C ../uldaq-env init conf build install -
Build the open62541 library into the
vendordirectory:git clone https://github.com/jeonghanlee/open62541-env ../open62541-env echo "INSTALL_LOCATION=${VENDOR_PATH}" > ../open62541-env/configure/CONFIG_SITE.local make -C ../open62541-env init conf build installBoth libraries are in the
vendordirectory:ls "${VENDOR_PATH}/lib"The output lists the libraries of both packages:
cmake libopen62541.so libopen62541.so.1 libopen62541.so.1.3.15 libuldaq.a libuldaq.la libuldaq.so libuldaq.so.1 libuldaq.so.1.2.1 pkgconfig -
Clone EPICS base and every module at its pinned tag or commit:
make initEach source tree lands in a
<name>-srcdirectory at the repository top. Module pins and dependencies lists the pins. -
Apply the carried upstream patches:
make patchThe output prints one
Patchingline per patch file. -
Write the site configuration of base and every module:
make confThe output is one empty line.
-
Build base and every module:
make buildBase installs into the installed tree as it builds, and each module installs after its build completes.
-
Complete the install of base and the modules, and add the
commonIocshfragments,setEpicsEnv.bash,resetEpicsEnv.bash, and the.versionsfile:make install -
Create the unversioned module links, such as
asynforasyn-4.46.0:make symlinkssetEpicsEnv.bashfinds thepvxsandpmactools through these links.
The Debian 12, Debian 13, and Rocky 8 CI workflows run make github.check
and then make install. github.check runs steps 5 through 8, step 10, and
the vars listing, with the module dependency gate between make conf and
make build.
Verification
-
List the top level of the installed tree:
LC_ALL=C make exist LEVEL=1With the vendor libraries of steps 1 through 4, the tree holds base, the modules, the vendor libraries, both environment scripts, and the version file:
<install_location>/1.4.0/debian-13/7.0.10 |-- .versions |-- base |-- modules |-- resetEpicsEnv.bash |-- setEpicsEnv.bash `-- vendor 4 directories, 3 files<install_location>is the value ofINSTALL_LOCATION. When you skip steps 1 through 4 because the vendor libraries are under/usr/local, the tree has novendordirectory, and the listing has novendorline.This listing shows a first install. Replacing existing setup or reset scripts also leaves backup files, which add entries to the listing.
LC_ALL=Cmakestreedraw its lines with American Standard Code for Information Interchange (ASCII) characters.make existusestreewhen it is installed andfindotherwise, so the drawing differs on a host withouttree. -
Run the runpath and environment gates:
make check.deps check.env > /dev/null 2>&1; echo $?The output is:
0Run the verification gates explains each gate and its report.
Set up a shell with the environment
Source setEpicsEnv.bash from the top of an installed tree to point the
current Bash shell at that tree’s Experimental Physics and Industrial Control
System (EPICS) base, modules, and tools.
Prerequisites
- An installed tree built with
make installandmake symlinks; see Build and install the environment. The script puts thepvxsandpmactools onPATHthrough the unversioned module links thatmake symlinkscreates. perlonPATHfor automatic architecture detection, or a fallback architecture supplied as described in step 3.
-
Source the script of the installed tree:
source <installed_tree>/setEpicsEnv.bash<installed_tree>is the path thatmake print-INSTALL_LOCATION_EPICSprints in the EPICS-env clone that built the tree.The script prints a summary of the values it set, followed by the full
PATHandLD_LIBRARY_PATHand the lineEnjoy Everlasting EPICS!. The summary starts with these lines:Set the EPICS Environment as follows: THIS Source NAME : setEpicsEnv.bash THIS Source PATH : <installed_tree> EPICS_BASE : <installed_tree>/base EPICS_HOST_ARCH : linux-x86_64 EPICS_MODULES : <installed_tree>/modulesThe script derives every path from its own location, so it needs no edit when the tree moves. To select another installed version, source that tree’s setup script. No location or release argument is required. It exports these variables:
Variable Value EPICS_PATHThe directory that holds the script EPICS_BASE$EPICS_PATH/baseEPICS_MODULES$EPICS_PATH/modulesEPICS_HOST_ARCHThe detected architecture, such as linux-x86_64, or the supplied fallbackPATHPrepends modules/pmac/bin/<arch>,modules/pvxs/bin/<arch>, andbase/bin/<arch>LD_LIBRARY_PATHPrepends base/lib/<arch><arch>is the value ofEPICS_HOST_ARCH. WhenEPICS_BASEis set before you source the script, the script first printsEPICS_BASE is defined aswith that value. It then removes the entries of that tree fromPATHandLD_LIBRARY_PATHand adds the entries of the sourced tree. Sourcing a second tree therefore replaces the known base, pvxs, and pmac paths of the first. Removal compares complete colon-delimited entries. Unrelated entries retain their bytes and order, including duplicates and empty entries. Repeating setup adds each managed directory once.Setup preserves independent settings such as
EPICS_CA_ADDR_LISTandEPICS_CA_AUTO_ADDR_LIST. It also works with Bash nounset enabled byset -u, and preserves the caller’s directory, shell options, and positional arguments. -
Optional: to set the same variables without the printed summary, pass
disable:source <installed_tree>/setEpicsEnv.bash disableIn the shell of step 1,
EPICS_BASEis set, so the script prints the tree that it replaces, followed by two empty lines:EPICS_BASE is defined as <installed_tree>/baseIn a shell where
EPICS_BASEis not set, the script prints one empty line. -
Optional: supply a fallback architecture when Perl or the base discovery scripts are unavailable:
source <installed_tree>/setEpicsEnv.bash linux-x86_64 disableThe interface is
[<fallback_arch>] [disable]. Automatic detection takes priority over the fallback.disablecontrols only the summary. Invalid argument forms return status 2. If the script cannot determine its tree or architecture, it returns status 1 with a diagnostic and preserves the existing managed environment. A discovery command that fails returns status 1 before the script replaces that environment. -
Optional: remove the environment from the shell by sourcing the installed reset script:
source <installed_tree>/resetEpicsEnv.bashThe output names the tree it removes:
EPICS_BASE is defined as <installed_tree>/base Reset ...The script removes the
base,pvxs, andpmacentries fromPATH, thebaseentry fromLD_LIBRARY_PATH, and unsetsEPICS_PATH,EPICS_BASE,EPICS_HOST_ARCH, andEPICS_MODULES. It also removes the known legacy extension executable entry and unsetsEPICS_EXTENSIONS. Missing path components prevent removal of the corresponding entry; variable unsetting also runs when base is absent. Repeated reset succeeds.Reset preserves independent CA settings and other unmanaged EPICS variables.
install.baseinstalls both setup and reset with mode 0644.
Verification
-
Check that the shell finds the EPICS tools in the installed tree:
command -v softIoc caget pvget pvxgetEach tool resolves inside
<installed_tree>:<installed_tree>/base/bin/linux-x86_64/softIoc <installed_tree>/base/bin/linux-x86_64/caget <installed_tree>/base/bin/linux-x86_64/pvget <installed_tree>/modules/pvxs/bin/linux-x86_64/pvxget
Add or bump a module
A module enters the Experimental Physics and Industrial Control System
(EPICS) environment through a pin in configure/RELEASE, and make derives its
clone, configuration, build, and install targets from that pin. This page
changes the pin of one module, or adds a module, and builds only that module.
The module set explains how the declarations
fit together. Run every command from the top of the EPICS-env checkout. The example bumps
caPutLog from dafb0b2 to 6f9eb3f.
Prerequisites
- EPICS base is installed at the install location; see Build and install the environment.
make initandmake confhave run in the checkout, so the source trees of the module’s dependencies and the top-levelRELEASE.localexist.- The module key, the module name, and the current pin of an existing module are listed in Module pins and dependencies.
-
Optional: To list the modules whose pin differs from the latest upstream tag or commit, survey the pins without changing any file:
tools/update-release.bash checkThe survey reads
configure/RELEASE, not the.localoverride files. The entry forcaPutLogreads:CAPUTLOG : UPDATE AVAILABLE Current: dafb0b2 Latest: 6f9eb3f Date: 2026-02-26 >> Diff Link: https://github.com/epics-modules/caPutLog/compare/dafb0b2...6f9eb3fThe command exits 0 when every pin was surveyed, 1 when a repository was unreachable or a pin has no repository Uniform Resource Locator (URL) comment, and 2 when a repository has no release tag.
-
Set the pin of the module.
a. To bump a module, set
SRC_TAG_<module_key>to the tag or commit to check out andSRC_VER_<module_key>to the version that names the install directory. Inconfigure/RELEASEthe change becomes the pin of the release; inconfigure/RELEASE.localit applies to this checkout only, because make readsconfigure/RELEASE.localafterconfigure/RELEASE:SRC_TAG_CAPUTLOG:=6f9eb3f SRC_VER_CAPUTLOG:=6f9eb3fA tag pin takes the form
tags/<tag>, such astags/R4-46, or the bare tag name, such as2-7-9forpmac.b. To add a module, add a block before the two
-includelines at the end ofconfigure/RELEASE. The comment line holds the repository URL thattools/update-release.bashsurveys:## https://github.com/<organization>/<module> SRC_NAME_<module_key>:=<module> SRC_TAG_<module_key>:=<tag_or_commit> SRC_VER_<module_key>:=<version><organization>is the GitHub organization or user that hosts the repository, such asepics-modules.<module_key>is an upper-case key of your choice, such asCAPUTLOG.<module>is the repository name; the source directory is<module>-src, and the module targets use<module>, such asbuild.<module>.<tag_or_commit>and<version>are the pin and the version as in sub-step a. -
If you are adding a module that is not hosted under
https://github.com/epics-modules, set its repository URL inconfigure/CONFIG_MODS, next to the otherSRC_GITURL_*lines:SRC_GITURL_<module_key>:=$(strip $(SRC_URL_<org_key>))/$(strip $(SRC_NAME_<module_key>))<org_key>is the upper-case key of an organization URL thatconfigure/RELEASEdefines. Most of these URLs are at the top of the file, such asSRC_URL_MD; a few are in the block of their module, such asSRC_URL_PMAC. When none matches, add a line such asSRC_URL_<org_key>:=https://github.com/<organization>to the block of the module. -
If you are adding a module, declare its build prerequisites and its configuration type. In
configure/CONFIG_MODS_DEPS, declare the prerequisites:<module>_DEPS:=null.base build.<dependency><module>_DEPSstarts withnull.baseand lists, in build order, thebuild.<dependency>target of each module that must be built first; a module that needs only EPICS base usesnull.basealone. Inconfigure/CONFIG_MODS_TYPES, declare the configuration type:<module>_CONF_TYPE:=custom<module>_CONF_TYPEisautoorcustom. Make stops every command withMissing <module>_CONF_TYPE declarationuntil this line exists. -
If you are adding a module, provide its configuration target.
a. For an
automodule, make generatesconf.<module>, which writesINSTALL_LOCATIONinto the module’sconfigure/CONFIG_SITE.local. Chooseautoonly when the module’s ownconfigure/CONFIG_SITEreads$(TOP)/configure/CONFIG_SITE.localand the module needs no dependency path; otherwise the module installs into its source tree. Three optional variables inconfigure/CONFIG_MODS_DEPSextend the generated target, as foriocStats,retools, andMCoreUtils:iocStats_CONF_RELEASE_LINES:=MAKE_TEST_IOC_APP=NO retools_CONF_SITE_LINES:=USR_CPPFLAGS += -DUSE_TYPED_RSET MCoreUtils_CONF_PLATFORM:=LinuxThe target writes
<module>_CONF_RELEASE_LINESinto the module’sconfigure/RELEASE.localand appends<module>_CONF_SITE_LINESto itsconfigure/CONFIG_SITE.local. When<module>_CONF_PLATFORMis set, the target acts only on a host whoseuname -soutput matches it.b. For a
custommodule, addconf.<module>andconf.<module>.showtoconfigure/RULES_MODS_CONFIG. The rule writes each dependency path from itsINSTALL_LOCATION_<module_key>variable, asconf.modbusdoes:conf.modbus: @echo "ASYN=$(INSTALL_LOCATION_ASYN)" > $(TOP)/$(SRC_PATH_MODBUS)/configure/RELEASE.local @echo "INSTALL_LOCATION:=$(INSTALL_LOCATION_MODBUS)" > $(TOP)/$(SRC_PATH_MODBUS)/configure/CONFIG_SITE.local conf.modbus.show: conf.release.modules.show cat -b $(TOP)/$(SRC_PATH_MODBUS)/configure/RELEASE.local cat -b $(TOP)/$(SRC_PATH_MODBUS)/configure/CONFIG_SITE.localAlso list
conf.<module>.showinQUERY_SHOW_TARGETSinconfigure/CONFIG_GOALS, so the target takes the query-only path.c. For a
custommodule, appendconf.<module>toMODS_ZERO_CUSTOM_VARSwhen the module needs only EPICS base, or toMODS_ONE_VARSwhen it needs other modules, inconfigure/RULES_MODS_CONFIG. Do not editMODS_ZERO_VARS: make builds it fromMODS_ZERO_CUSTOM_VARSand the generatedautotargets.These lists group configuration targets;
<module>_DEPScontrols build order. QPC and sscan belong toMODS_ONE_VARSbecause their effective configuration namesASYNandSNCSEQ, respectively. -
If another module’s configuration names the added module by its key, map the key to the module name in
configure/CONFIG_MODS_AUDIT, so the dependency audit resolves it:AUDIT_MODULE_ALIASES+=<module_key>=<module> -
Optional: To force regeneration of
configure/MODULESGEN.mk, which holds each module’s repository URL, source directory, and install directory, run:make reconf.modulesMake regenerates this file automatically when
configure/RELEASEorconfigure/CONFIG_SITEchanges, or when the effective module triples change. Creating, editing, or removing a pin override inconfigure/RELEASE.localor../RELEASE.localtakes effect on the next action invocation. Variable queries use the effective pins immediately without generating or rewriting the cache. This optional command removes the generated files underconfigure/and regeneratesMODULESGEN.mkfrom the current settings. -
Print the install directory of the module:
make print-INSTALL_LOCATION_CAPUTLOGThe last path component carries the version from step 2:
<install_location>/1.4.0/debian-13/7.0.10/modules/caPutLog-6f9eb3f<install_location>is theINSTALL_LOCATIONof the checkout. -
If you are bumping a module, remove its source tree, because the clone target skips a directory that exists:
rm -rf caPutLog-src -
Clone the module and check out its pin:
make CAPUTLOGThe target is the module key. The last line of the output names the checked-out commit:
HEAD is now at 6f9eb3f Bumped compatibility to EPICS 3.15.9 in the documentation -
Write the configuration files of the module:
make conf.caPutLogA few configuration targets differ from the module name, such as
conf.sncseqforsequencer; see Source configuration targets. -
Check the declared dependencies against the module source:
make check.module-deps MODULE=caPutLogThe report ends with the findings of the module:
Module: caPutLog Declared: null.base Observed: Findings: noneThe command exits 2 when the source uses a module that
<module>_DEPSdoes not declare, or a token that no module name or alias matches. -
Build and install the module:
make build.caPutLogThe target builds the modules in
<module>_DEPSfirst, and each module installs as it builds. -
Point the unversioned link of the module at the install directory from step 8:
make symlink.caPutLog -
If other modules list
build.<module>in their_DEPS, reconfigure and rebuild them, because their configuration names the install directory of the pin they were built with; see Build and install the environment.
Verification
List the installed module directory:
make ls.INSTALL_LOCATION_CAPUTLOG
The directory holds the installed module:
configure
dbd
include
lib
Show the link in the modules directory:
make ls.INSTALL_LOCATION_MODS LSOPTS=-l | grep caPutLog
<user> is your user and group. The link points at the directory of the pin
from step 2. A bump does not
remove the install directory of the replaced pin, so caPutLog-dafb0b2
stays in place:
lrwxrwxrwx 1 <user> <user> 18 Sep 26 23:49 caPutLog -> ./caPutLog-6f9eb3f
drwxrwxr-x 6 <user> <user> 120 Sep 26 23:49 caPutLog-6f9eb3f
drwxrwxr-x 6 <user> <user> 120 Sep 26 22:48 caPutLog-dafb0b2
Carry an upstream fix as a patch
EPICS-env carries upstream fixes that the pinned tag of the Experimental
Physics and Industrial Control System (EPICS) base or of pvxs does not
contain. Each fix is a patch file in patch/, which make patch applies
after make init clones the sources. A patch file that follows the naming
below joins the carry set without any Makefile change; a patch for any other
module has its own targets, listed in
Upstream patch targets.
The example regenerates the pvxs carry
1.5.2-12-cc7bc72-synccancel-diag.p0.patch from upstream commit cc7bc72.
Upstream patch carry explains the
carry set. Run every command from the top of the EPICS-env checkout.
Prerequisites
make initandmake patchhave run, soepics-base-srcandpvxs-srchold the pinned sources with the carry set applied.- The fix is merged upstream as one commit or as a contiguous range of commits.
-
Print the pinned version, which is the first field of every carry file name of that source:
make print-SRC_VER_PVXSThe command prints the version:
1.5.2For EPICS base, print
SRC_VER_BASEinstead. -
Revert the carry set, so the source tree returns to the pin:
make patch.pvxs.commit.revertThe target reverses each file in the exact reverse of the apply order and stops at the first file that does not revert. For EPICS base, run
make patch.base.pr.revert. -
Check that the source tree matches the pin:
git -C pvxs-src status --shortThe command prints nothing. In
epics-base-src, the command lists onlyconfigure/CONFIG_SITE_ENVandconfigure/os/CONFIG_SITE.linux-x86_64.linux-x86_64, whichmake confwrites. -
Update the clone with the upstream commits:
git -C pvxs-src fetch origin -
Choose the file name, so that the sorted file names give the apply order:
Source File name Unit EPICS base <base_version>-pr<pr_number>-<slug>.p0.patchA merged pull request EPICS base <base_version>-<sequence>-<sha7>-<slug>.p0.patchA commit merged without a pull request pvxs <pvxs_version>-<sequence>-<sha7>-<slug>.p0.patchA commit <base_version>and<pvxs_version>are the pinned versions from step 1.<pr_number>is the number of the pull request, zero-padded to four digits.<sequence>is a two-digit number that follows the upstream merge order.<sha7>is the first seven characters of the commit hash, and<slug>is a short lower-case description. Make applies the files in byte order, so every<sequence>file of EPICS base applies before everyprfile. Make selects only the files whose name starts with the pinned version, so a pin bump leaves every other carry file out of the set. -
Write the change of the upstream commits as a patch without path prefixes, which
patch -p0applies from the top of the source tree:git -C pvxs-src diff --no-prefix cc7bc72^ cc7bc72 > patch/1.5.2-12-cc7bc72-synccancel-diag.p0.patchcc7bc72^stands for<first_commit>^, the parent of the first commit of the fix, andcc7bc72stands for<last_commit>, its last commit. For a fix of one commit, both name the same commit. For EPICS base, run the command againstepics-base-src. -
Apply the carry set, including the file from step 6:
make patch.pvxs.commit.applyThe output ends with the file from step 6:
Patching pvxs-src with the file : <checkout>/patch/1.5.2-12-cc7bc72-synccancel-diag.p0.patch patching file ioc/pvalink_channel.cpp patching file src/clientdiscover.cpp patching file src/clientget.cpp patching file src/clientintrospect.cpp patching file src/clientmon.cpp patching file src/evhelper.cpp patching file src/evhelper.h<checkout>is the path of the EPICS-env checkout. The target stops with a non-zero exit status at the first file that does not apply. If the fix does not apply on top of the files that sort before it, edit the fix against the pinned source before you carry it. For EPICS base, runmake patch.base.pr.apply.
Verification
Revert the carry set, print the exit status of make, and check the source tree:
make patch.pvxs.commit.revert > /dev/null; echo $?
git -C pvxs-src status --short
The first command prints the exit status, and the status command prints nothing, as in step 3:
0
Apply the carry set again and print the exit status of make:
make patch.pvxs.commit.apply > /dev/null; echo $?
The command prints 0 when every carry file applies:
0
When a carry file does not apply, the target stops at that file, and make reports the error and exits 2:
make: *** [<checkout>/configure/RULES_PATCH:141: patch.pvxs.commit.apply] Error 1
2
For EPICS base, the same check uses make patch.base.pr.revert,
epics-base-src, and make patch.base.pr.apply.
Verify a fix against an installed tree
Some module fixes show their effect only with the hardware that a running
input/output controller (IOC) drives. To verify such a fix, you build the
fixed module and a copy of that IOC beside the installed tree, and run the
copy in place of the production IOC. The procedure reads the installed tree
and never writes to it. tools/verify_fix_build.bash builds both copies
and checks their links, and tools/pv_snapshot.bash compares process
variable (PV) values before, during, and after the run. The example verifies a StreamDevice fix with a consumer
IOC named sdemo.
Prerequisites
- The installed tree
<tree>holdsbase/,modules/,vendor/, andsetEpicsEnv.bash; see the installed tree. - An EPICS-env checkout
<env_checkout>at the release that<tree>was built from. - The fix as a patch with
a/andb/path prefixes, such as the output ofgit diffin a fork of the module, and the commit<base_commit>of the module that the patch applies to. - The consumer IOC repository and the commit that production runs. The IOC
configure/RELEASEnames the module by its key, such asSTREAM. - A PV list file with one PV name per line, holding the PVs whose values the
fix must not change. Blank lines and
#comments are ignored. cagetinPATH, and the authority to stop and restart the production IOC.
-
In the checkout, clone the module source, which also generates
configure/MODULESGEN.mk:make -C <env_checkout> STREAM<env_checkout>is the path of the EPICS-env checkout, andSTREAMis the module key, theKeycolumn of Module pins and dependencies. -
Write the configuration of the module in the checkout:
make -C <env_checkout> conf.release.modules conf.StreamDeviceThe tool reads the module’s own settings, such as vendor paths, from this configuration.
On Ubuntu 26, check the generated C17 setting before building:
grep -Fxc 'USR_CFLAGS += -std=gnu17' <env_checkout>/StreamDevice-src/configure/CONFIG_SITE.localA checkout with per-module C17 configuration prints
1. Release1.4.0does not add this setting throughconf.StreamDevice; the check prints0and exits 1. For that result, append the setting:printf '%s\n' 'USR_CFLAGS += -std=gnu17' >> <env_checkout>/StreamDevice-src/configure/CONFIG_SITE.localRepeat the check; it must print
1before you continue. Resolve any read error or duplicate setting first. Repeat this check after rerunning the configuration command, because it rewrites the file. Skip this C17 check and append on other operating systems. -
Create the scratch directories, named after the installed module and the IOC binary:
mkdir -p <scratch_dir>/StreamDevice <scratch_dir>/sdemo<scratch_dir>is a directory outside<tree>and outside any checkout. The module directory name must equal the directory name in<tree>/modules, and the IOC directory name must equal the IOC binary name. -
Export the module source at the commit the fix applies to:
git -C <module_source> archive <base_commit> | tar -x -C <scratch_dir>/StreamDevice<module_source>is a clone of the module that holds<base_commit>, such as the fork of the fix or<env_checkout>/StreamDevice-src.<base_commit>must be the pin of the installed module,SRC_TAG_STREAMin the example; otherwise the copy carries changes unrelated to the fix. -
If the fix is not one of the patch files in
<env_checkout>/patch, apply it:patch -d <scratch_dir>/StreamDevice -p1 < <fix_patch><fix_patch>is the path of the fix patch. -
Apply each file from
<env_checkout>/patchthat thepatchtarget applies to the module on this platform, in the order of that target.configure/RULES_SRCdefines thepatchtarget, andconfigure/RULES_PATCHnames the file that each module target applies. ForStreamDevice, the target applies one file:patch -d <scratch_dir>/StreamDevice --ignore-whitespace -p0 < <env_checkout>/patch/StreamDevice-no-vxi11.p0.patchThe directory also holds files that no target applies on this platform, such as
mca-libnet.p0.patch, which applies only on macOS. Upstream patch targets lists the patch target of each module and the name pattern of each carry set. -
Export the consumer IOC at its production commit:
git -C <ioc_repository> archive <ioc_commit> | tar -x -C <scratch_dir>/sdemo<ioc_repository>is a clone of the IOC repository, and<ioc_commit>is the commit production runs. -
Load the environment of the installed tree:
source <tree>/setEpicsEnv.bashThe tool takes the tree from
EPICS_BASE, which this script sets to<tree>/base. -
Build the module and the IOC copy against the tree:
<env_checkout>/tools/verify_fix_build.bash <scratch_dir>/StreamDevice <scratch_dir>/sdemoThe tool writes
configure/RELEASE.localandconfigure/CONFIG_SITE.localin both copies. The module takes its dependencies from<tree>/modules/StreamDevice/configure/RELEASE.localand its own settings fromconf.StreamDevice.showin the checkout, with vendor paths pointed at<tree>/vendor. The IOC takes the module from<scratch_dir>/StreamDevicethrough the module key.The tool then runs four checks. The copy must rebuild every library that the installed module provides, and each rebuilt library must resolve the same shared libraries. Runpaths must name only
<tree>,$ORIGIN, and, for the IOC, the module copy. The IOC must link the module from the copy. Finally, the build must write no file under<tree>. In this output,<tree>and<scratch_dir>stand for the paths of the run:[verify_fix_build.bash] production tree: <tree> [verify_fix_build.bash] module: <scratch_dir>/StreamDevice [verify_fix_build.bash] IOC: <scratch_dir>/sdemo [verify_fix_build.bash] module dependencies from the production tree: 3 entries [verify_fix_build.bash] dependency cross-check: EPICS-env checkout matches the installed module [verify_fix_build.bash] module build: OK [verify_fix_build.bash] module libraries (libstream.so): runpath only the production tree [verify_fix_build.bash] module libraries resolve the same shared libraries as production [verify_fix_build.bash] IOC build: OK; links 1 module library from the module directory [verify_fix_build.bash] production tree untouched ---------------------------------------------------------------- Steps 2-3 PASSED: StreamDevice and sdemo against <tree> Build logs: <scratch_dir>/module-build.log, <scratch_dir>/ioc-build.log ---------------------------------------------------------------- Steps 4-5 (manual, one IOC at a time): 1. tools/pv_snapshot.bash capture -l <pvlist> -o before.txt (production running) 2. stop the production IOC; start the copy from its iocBoot with the defect visible 3. observe the defect check; capture after.txt 4. stop the copy; restart production; capture restored.txt 5. tools/pv_snapshot.bash compare -t <tolerance> before.txt after.txt tools/pv_snapshot.bash compare -t <tolerance> before.txt restored.txtThe closing lines summarize steps 10 to 18 and the verification of this page. The tool exits 0 when every check passes or on
--help, and 2 when it does not receive exactly two arguments. It exits 1 on any other failure, such as a missing directory, an unsetEPICS_BASE, a failed build, or a failed check. When the checkout’s module dependencies differ from those of the installed module, the tool shows the difference and asks for confirmation on the terminal. The file<scratch_dir>/.startedmarks the start of the run. -
In the copy’s
iocBoot/<instance>/st.cmd, remove any setting that hides the defect, such as anasynSetTraceMaskcall that silences errors.<instance>is the directory underiocBootthat holds the startup script of the IOC, such asiocsdemo. -
While the production IOC runs, capture the
beforesnapshot:<env_checkout>/tools/pv_snapshot.bash capture -l <pv_list> -o before.txt<pv_list>is the PV list file. The command reports the count:pv_snapshot.bash: 3 PVs captured to before.txt, 0 not connectedA PV that does not connect is written as
__DISCONNECTED__. The option-w <seconds>sets the Channel Access (CA) timeout, 2.0 seconds by default. -
Stop the production IOC.
-
Start the copy from
<scratch_dir>/sdemo/iocBoot/<instance>, the directory of step 10. -
Observe the defect check with the copy running; the defect must not appear.
-
While the copy runs, capture the
aftersnapshot:<env_checkout>/tools/pv_snapshot.bash capture -l <pv_list> -o after.txt -
Stop the copy.
-
Restart the production IOC.
-
Capture the
restoredsnapshot:<env_checkout>/tools/pv_snapshot.bash capture -l <pv_list> -o restored.txt
Verification
Compare the snapshot taken while the copy ran, and the snapshot taken after
the restart, with the before snapshot:
<env_checkout>/tools/pv_snapshot.bash compare -t 0.001 before.txt after.txt
<env_checkout>/tools/pv_snapshot.bash compare -t 0.001 before.txt restored.txt
-t sets the largest numeric difference that counts as WITHIN; choose it
before the run. Each command prints one line per PV and a summary:
SAME VFD:Setpoint 1.5 1.5
SAME VFD:Mode 2 2
SAME VFD:Label demo demo
# PVs 3: SAME 3, WITHIN 0, DIFF 0, MISSING 0, DISCONN 0; tolerance 0.001
Each command exits 0 when every PV is SAME or WITHIN, 1 when a PV is
DIFF, MISSING, or DISCONN, and 2 on a usage or runtime error. Check
that no file under the tree was written since the run started:
find <tree> -type f \( -newer <scratch_dir>/.started -o -cnewer <scratch_dir>/.started \)
The command prints nothing.
Run the verification gates
Run the three verification gates to confirm that the module dependencies match
the sources, that the installed libraries find each other, and that the
installed environment script adds no LD_LIBRARY_PATH entry under
pvxs/bundle.
Prerequisites
- An EPICS-env clone configured with
make init,make patch, andmake conffor the module dependency gate. - An installed tree built with
make installandmake symlinksfor the runpath and environment gates; see Build and install the environment. readelfonPATHfor the runpath gate.
-
To compare each module’s declared dependencies with the dependencies found in its sources, run the module dependency gate from the repository top:
make check.module-depsThe report prints one block per module. For each module,
Declaredlists the build targets it waits for,Observedlists the dependencies found in itsRELEASE.local, makefiles, databases, and sources, andFindingslists the differences. Anundeclared-observedorunknownfinding makes the gate fail with exit status 2. Adeclared-unobservedfinding is reported and does not fail the gate. Run this gate aftermake confand beforemake build, the place it takes inmake github.check. -
Optional: to audit one module, add
MODULE:make check.module-deps MODULE=asynOn a configured clone before
make build, the output is:Module Dependency Audit Strict: YES Source state: generated RELEASE.local files are used when present. Platform: Linux Strict policy: undeclared-observed and unknown findings fail. Module: asyn Declared: null.base build.sequencer build.sscan build.calc Observed: required sequencer asyn-src/configure/RELEASE.local:1 SNCSEQ required sscan asyn-src/configure/RELEASE.local:2 SSCAN required calc asyn-src/configure/RELEASE.local:3 CALC optional sequencer asyn-src/testIPServerApp/src/Makefile:20 seq optional sequencer asyn-src/testIPServerApp/src/Makefile:20 pv optional sequencer asyn-src/testIPServerApp/src/Makefile:31 seq optional sequencer asyn-src/testIPServerApp/src/Makefile:31 pv optional calc asyn-src/testEpicsApp/src/Makefile:24 calc optional sscan asyn-src/testEpicsApp/src/Makefile:27 sscan optional sequencer asyn-src/testEpicsApp/src/Makefile:30 seq optional sequencer asyn-src/testEpicsApp/src/Makefile:30 pv optional calc asyn-src/testEpicsApp/src/Makefile:44 calc optional sscan asyn-src/testEpicsApp/src/Makefile:48 sscan optional sequencer asyn-src/testEpicsApp/src/Makefile:51 seq optional sequencer asyn-src/testEpicsApp/src/Makefile:51 pv optional iocStats asyn-src/testApp/src/Makefile:15 test.dbd optional sequencer asyn-src/makeSupport/app/_NAME_App/src/Makefile:31 seq optional sequencer asyn-src/makeSupport/app/_NAME_App/src/Makefile:31 pv optional calc asyn-src/asyn/Makefile:193 calc external ftdi1 asyn-src/asyn/Makefile:268 ftdi1 external ftdi1 asyn-src/asyn/Makefile:270 ftdi1 external ftdi asyn-src/asyn/Makefile:275 ftdi external ftdi asyn-src/asyn/Makefile:277 ftdi optional calc asyn-src/testEpicsApp/Db/devOctetCalc.db:6 scalcout Findings: noneThe
Observedlines follow the order in which the file system lists the files, so their order can differ. The audit also reads files that the build generates, so a built clone can list differentObservedlines.make audit.module-depsprints the same report and exits 0 whatever it finds. -
To scan the installed executables and shared libraries for runpath defects, run the runpath gate:
make check.depsThe scan ends with a summary of counts. For the verified Debian 13 installed tree, the summary is:
-------------------------------------------------------- >> BIN: Total Files with RPATH / ALL: 0 / 85 >> SO: Total Files with RPATH / ALL: 0 / 69 >> BIN: Total Files with ABSPATH / ALL: 0 / 85 >> SO: Total Files with ABSPATH / ALL: 0 / 69 >> SO: Total Files with LOSTORG / ALL: 0 / 69 --------------------------------------------------------The block shows the text of the summary. The tool wraps each defect count in terminal color codes, which a terminal shows as color and a file or pipe keeps as escape sequences.
Each canonical executable is counted once, including executables reached through both versioned module directories and unversioned links. File counts depend on the installed module set. An empty tree path or a path that is not a directory exits 2 and directs the caller to set
INSTALL_LOCATION_EPICSor pass a valid<installed_tree>.The gate reads the dynamic section of the executables in
bin/linux-x86_64and the shared libraries inlib/linux-x86_64of base and every module, and of the shared libraries invendor/lib. Each row counts one defect:RPATH: the file carries a run-time search path (RPATH), which the loader searches beforeLD_LIBRARY_PATH, instead of a run-time library search path (RUNPATH).ABSPATH: the search path holds an absolute directory.LOSTORG: the shared library needs a library of the installed tree, and its search path lacks$ORIGIN, the loader token for the directory of the file itself.
Any nonzero count makes the gate exit with status 2. A
NOTEline above the summary marks a search path that holds a standard system directory such as/usr/lib; it does not count.make audit.depsprints the same scan and exits 0.An existing tree directory can still contain no files in the scan locations. Check that the
ALLcounts match the components you installed; a zero-defect result alone does not prove a complete installation. -
To check the library paths that the installed
setEpicsEnv.bashadds, run the environment gate:make check.envThe output is:
Inspecting <installed_tree>/setEpicsEnv.bash EPICS_MODULES <installed_tree>/modules EPICS_HOST_ARCH linux-x86_64 OK: no pvxs/bundle path in LD_LIBRARY_PATH findings: 0<installed_tree>is the path thatmake print-INSTALL_LOCATION_EPICSprints. The gate sources the script in a clean child shell and reports eachLD_LIBRARY_PATHentry that points at apvxs/bundledirectory, which the build never creates. The tool exits with status 2 on a finding and with status 3 when it cannot inspect the tree, such as beforemake install;makethen reportsError 2orError 3and exits with status 2.make audit.envprints the same report, exits 0 on a finding, and skips the check when the tree is absent.
Verification
-
Run all three gates and print the exit status:
make check.module-deps check.deps check.env > /dev/null 2>&1; echo $?The output is:
0makestops at the first gate that fails, so0means that all three passed.
Make targets by purpose
lists every verification target, and
Variables set on the command line
lists MODULE, FORMAT, and PLATFORM.
Load common iocsh fragments in an IOC
Add the site services of the common iocsh fragments to an Experimental
Physics and Industrial Control System (EPICS) input/output controller (IOC)
built against an installed tree. The IOC application in these steps is
named demo. It loads the iocLog, serial, caPutLog, autosave, reccaster, and
linStat fragments, and leaves out iocStatsAdmin, whose records collide with
linStat; see
Common iocsh fragments.
Prerequisites
- An installed tree built with
make install, which holds the fragments inmodules/commonIocsh/iocsh; see Build and install the environment. - An IOC application whose
iocBoot/<ioc_name>directory has aMakefilethat writesenvPaths, such as one thatmakeBaseApp.pl -icreates. - For
iocLog.iocshandcaPutLog.iocsh: a log server, such asiocLogServerfrom EPICS base, listening on Transmission Control Protocol (TCP) port 7004 of<log_host>. - For
setSerialParams.iocsh: a serial device that the IOC can open.
-
In the IOC’s
configure/RELEASE, or theconfigure/RELEASE.localfile it includes, name EPICS base and each module with the macro that the fragments read:MODULES = <installed_tree>/modules AUTOSAVE = $(MODULES)/autosave RECCASTER = $(MODULES)/recsync LINSTAT = $(MODULES)/linStat CAPUTLOG = $(MODULES)/caPutLog ASYN = $(MODULES)/asyn EPICS_BASE = <installed_tree>/base<installed_tree>is the path thatmake print-INSTALL_LOCATION_EPICSprints in the EPICS-env clone that built the tree. List only the modules whose fragments the IOC loads. Common iocsh fragment macros names the module each fragment needs. -
In the application’s
src/Makefile, add the database definition (DBD) files and libraries of those modules before the linedemo_LIBS += $(EPICS_BASE_IOC_LIBS):demo_DBD += caPutLog.dbd asSupport.dbd reccaster.dbd linStat.dbd system.dbd demo_DBD += asyn.dbd drvAsynSerialPort.dbd demo_LIBS += caPutLog autosave reccaster linStat asynThe
makeBaseApp.pltemplate already addsbase.dbdand$(EPICS_BASE_IOC_LIBS).system.dbdregisters the iocshsystemcommand, whichautosave.iocshuses to create its directories. -
To build the IOC against the installed modules, run this command from the top of the IOC application:
make CHECK_RELEASE=NOThe installed modules keep the
configure/RELEASEfiles of their upstream sources, which name a differentEPICS_BASE. With the release check on, the build stops atDefinition of EPICS_BASE conflicts with CAPUTLOG support. The build writesiocBoot/<ioc_name>/envPaths, which setsIOCto<ioc_name>and sets each module macro of step 1. -
Optional: to configure serial ports, create a serial configuration file in
iocBoot/<ioc_name>, such asserialPorts.cmd, with onesetSerialParams.iocshline for each port:iocshLoad("$(IOCSH_TOP)/iocsh/setSerialParams.iocsh", "PORT=S1,BAUD=19200,BITS=8,STOP=2,PARITY=odd")PORTis the asyn port name that the startup script gives todrvAsynSerialPortConfigure. -
To let caPutLog see puts, create an access security configuration file (ACF) in
iocBoot/<ioc_name>, such asdemo.acf, that marks writes withTRAPWRITE:ASG(DEFAULT) { RULE(1, READ) RULE(1, WRITE, TRAPWRITE) } -
In
iocBoot/<ioc_name>/st.cmd, setIOCSH_TOPand load the fragments between the DBD registration andiocInit:#!../../bin/linux-x86_64/demo < envPaths epicsEnvSet("IOCSH_TOP", "<installed_tree>/modules/commonIocsh") dbLoadDatabase("$(TOP)/dbd/demo.dbd") demo_registerRecordDeviceDriver(pdbbase) iocshLoad("$(IOCSH_TOP)/iocsh/iocLog.iocsh", "IOC=$(IOC),LOG_INET=<log_host>") drvAsynSerialPortConfigure("S1", "<tty_device>", 0, 0, 0) iocshLoad("$(IOCSH_TOP)/iocsh/serial.iocsh", "SERIAL_ENABLE=,SERIAL_CONFIG=$(TOP)/iocBoot/$(IOC)/serialPorts.cmd") asSetFilename("$(TOP)/iocBoot/$(IOC)/demo.acf") iocshLoad("$(IOCSH_TOP)/iocsh/caPutLog.iocsh", "LOG_INET=<log_host>") iocshLoad("$(IOCSH_TOP)/iocsh/autosave.iocsh", "IOC=$(IOC),AS_TOP=<as_top>") iocshLoad("$(IOCSH_TOP)/iocsh/reccaster.iocsh", "IOC=$(IOC)") iocshLoad("$(IOCSH_TOP)/iocsh/linStat.iocsh", "IOC=$(IOC)") iocInit()<log_host>is the address of the log server.<tty_device>is the serial device path, such as/dev/ttyS0.<as_top>is a writable directory for the autosave files.
IOCSH_TOPnames the directory that holdsiocsh, becauselinStat.iocshloads its host and process sub-fragments from$(IOCSH_TOP)/iocsh.iocLog.iocshloads first so that the log server receives the messages of the device setup that follows.serial.iocshloads after the port it configures exists. Leave out the lines of any service the IOC does not use; to skip serial setup, leave outSERIAL_ENABLE=.
Verification
With the log server of the prerequisites listening, boot the IOC of steps 1 to 6 and check its output:
-
Change to the boot directory of the IOC:
cd <ioc_top>/iocBoot/<ioc_name><ioc_top>is the top directory of the IOC application. -
Start the IOC with
st.cmd, write its output toboot.log, and let it exit at the end of input:../../bin/linux-x86_64/demo st.cmd < /dev/null > boot.log 2>&1 -
Show the log client, serial, caPutLog, and autosave lines of the boot:
grep -E 'log client|iocRun|caPutLog:|asynSetOption|afterIocRunning' boot.logThe output of the IOC
iocdemois:log client: connected to log server at '<log_host>:7004' asynSetOption("S1", -1, "baud", "19200") asynSetOption("S1", -1, "bits", "8") asynSetOption("S1", -1, "stop", "2") asynSetOption("S1", -1, "parity", "odd") afterIocRunning("caPutLogInit('<log_host>:7004', 0)") afterIocRunning("makeAutosaveFileFromDbInfo('<as_top>/iocdemo/req/settings.req','autosaveFields')") afterIocRunning("makeAutosaveFileFromDbInfo('<as_top>/iocdemo/req/values_pass0.req','autosaveFields_pass0')") afterIocRunning("makeAutosaveFileFromDbInfo('<as_top>/iocdemo/req/values_pass1.req','autosaveFields_pass1')") afterIocRunning("create_monitor_set('settings.req','5')") afterIocRunning("create_monitor_set('values_pass0.req','5')") afterIocRunning("create_monitor_set('values_pass1.req','10')") iocRun: All initialization complete sevr=info caPutLog: successfully initialized log client: connected to log server at '<log_host>:7004' afterIocRunning: caPutLogInit('<log_host>:7004', 0) afterIocRunning: makeAutosaveFileFromDbInfo('<as_top>/iocdemo/req/settings.req','autosaveFields') afterIocRunning: makeAutosaveFileFromDbInfo('<as_top>/iocdemo/req/values_pass0.req','autosaveFields_pass0') afterIocRunning: makeAutosaveFileFromDbInfo('<as_top>/iocdemo/req/values_pass1.req','autosaveFields_pass1') afterIocRunning: create_monitor_set('settings.req','5') afterIocRunning: create_monitor_set('values_pass0.req','5') afterIocRunning: create_monitor_set('values_pass1.req','10') 7.0.10 > caPutLog: disabledThe two
log client: connectedlines show that iocLog and caPutLog each reach the log server. TheasynSetOptionlines show thatserial.iocshran the serial configuration file. The lines withafterIocRunning(are the commands that caPutLog and autosave register beforeiocInit, and the lines withafterIocRunning:show each one running afteriocInit.caPutLog: successfully initializedshows that the logger started, andcaPutLog: disabledshows it stopping when the IOC exits. The IOC writes to both standard output and standard error, soboot.logdoes not keep the order in which these lines run. Some of these lines carry terminal color codes, which the output above leaves out.
Run the fragment verification suite
Check the common iocsh fragments of an installed tree against running
Experimental Physics and Industrial Control System (EPICS) input/output
controllers (IOCs). The suite has two parts in examples/commonIocsh:
verify_caputlog.py checks caPutLog.iocsh through the example IOC, and
the scripts in tests/ check every fragment through an external test IOC
named tc32sim.
Common iocsh fragments explains
what each fragment does.
Prerequisites
- An installed tree built with
make install; see Build and install the environment. - An EPICS-env clone, which holds the example IOC in
examples/commonIocsh. - Python 3 and
stdbufonPATH. - Transmission Control Protocol (TCP) port 7004 free on the local host.
verify_caputlog.pyfails a case that finds another process on that port. - For the optional
tests/scripts in step 5: a checkout of https://github.com/jeonghanlee/tc32sim,socat,ss, and free TCP ports 7011 and 7013.
-
In
<example_dir>/configure/RELEASE.local, set the installed EPICS base and caPutLog module:EPICS_BASE = <installed_tree>/base CAPUTLOG = <installed_tree>/modules/caPutLog<example_dir>is theexamples/commonIocshdirectory of the EPICS-env clone.<installed_tree>is the path thatmake print-INSTALL_LOCATION_EPICSprints in the EPICS-env clone that built the tree.
-
Build the example IOC:
make -C <example_dir> CHECK_RELEASE=NOThe installed caPutLog module keeps the
configure/RELEASEfile of its upstream source, which names a differentEPICS_BASE. With the release check on, the build stops atDefinition of EPICS_BASE conflicts with CAPUTLOG support. The build writes<example_dir>/bin/linux-x86_64/commonIocshExample. -
Change to the installed tree, so that the script arguments stay short:
cd <installed_tree> -
Run the caPutLog checks against the installed fragment:
python3 <example_dir>/verify_caputlog.py --base base --iocsh-top modules/commonIocsh --output <evidence_dir>--basenames the EPICS base directory that providescaputandiocLogServer.--iocsh-topnames thecommonIocshdirectory that holds theiocshdirectory of the fragments, and the script readsiocsh/caPutLog.iocshbelow it. The default iscommonIocshof the clone that holds the script.<evidence_dir>is the absolute path of a directory that does not exist; the script creates it and refuses an existing one.
Two more options have defaults:
--archsets the architecture directory of the IOC and base binaries,linux-x86_64, and--timeoutsets the seconds to wait for each expected event,30.The script prints one line per case and the evidence directory:
[ PASS ] defaults: observed expected behavior [ PASS ] all_puts: observed expected behavior [ PASS ] unfiltered: observed expected behavior [ PASS ] disabled: observed expected behavior [ PASS ] invalid_option: observed expected behavior [ PASS ] missing_host: observed expected behavior Evidence: <evidence_dir>For each case, the script starts
iocLogServerfrom EPICS base and the example IOC, which loadsiocsh/caPutLog.iocshbelow--iocsh-top. It then writes the test record withcaputfrom EPICS base and reads what the log server received. It removes every inheritedEPICS_CA_*,EPICS_CAS_*, andEPICS_IOC_LOG_*variable and gives each case its own Channel Access (CA) port on127.0.0.1. The cases are:Case Macros passed to caPutLog.iocshExpected behavior defaultsLOG_INET=127.0.0.1Port 7004, option 0: puts of 17, 17, and 29 give new=17 old=0andnew=29 old=17, and the repeated 17 adds no lineall_putsLOG_INET, a freeLOG_INET_PORT,OPTION=1The repeated 17 adds a line unfilteredLOG_INET, a freeLOG_INET_PORT,OPTION=2The repeated 17 adds a line disabledLOG_INET, a freeLOG_INET_PORT,OPTION=-1The IOC prints caPutLogInit config: Disabled, and the log server receives no lineinvalid_optionLOG_INET, a freeLOG_INET_PORT,OPTION=9The IOC prints caPutLogInit config: Unknown (must be -1, 0, 1, or 2), and the log server receives no linemissing_hostnone The IOC prints macLib: macro LOG_INET is undefined, and the log server receives no lineIn the cases that log, the script also checks that
caPutLogInitruns afteriocRun: All initialization complete. A case that waits for a result fails after 30 seconds; a case that expects no line watches for 12 seconds.The evidence directory holds
summary.jsonand one directory per case.summary.jsonnames the base, the IOC binary, the fragment path, the Secure Hash Algorithm 256-bit (SHA-256) digest of the fragment, and each case result. Each case directory holdsinputs.json,ioc.log,server.log,received.log, andclients.log. The script exits with 0 when every case passes and 1 when a case fails. -
Optional: to check every fragment through the tc32sim test IOC, run
run_all.shfrom the top of the EPICS-env clone whose example IOC steps 1 and 2 built:export DIST_TOP=<installed_tree> TC32SIM=<tc32sim_dir> export COMMONIOCSH=<installed_tree>/modules/commonIocsh bash examples/commonIocsh/tests/run_all.shDIST_TOPnames the installed tree that providesbase, the modules,iocLogServer, andcaput.<tc32sim_dir>is the tc32sim checkout.COMMONIOCSHnames thecommonIocshdirectory that holds theiocshdirectory of the fragments. The scripts setIOCSH_TOPto it and load$(IOCSH_TOP)/iocsh/<fragment>.iocsh.
The defaults of these three variables are paths on the developer’s host, so set all three. The scripts read them, and these optional variables, through
tests/common.sh:Variable Default Meaning ARCHlinux-x86_64Architecture directory of the binaries SKIP_REBUILD01leavesconfigure/RELEASE.localof tc32sim unchanged and uses its built binaryKEEP_WORKSPACE01keeps the temporary directory ofverify_caputlog.shandverify_integrated.shThe scripts use the tc32sim checkout as follows:
- They run
<tc32sim_dir>/bin/<ARCH>/tc32simfrom<tc32sim_dir>/iocBoot/ioctestlab-tc32sim, whoseenvPathssetsIOCtoioctestlab-tc32sim. - Unless
SKIP_REBUILD=1, each script exceptverify_caputlog.shoverwrites<tc32sim_dir>/configure/RELEASE.localwithEPICS_BASEand the module macros it needs, then runsmake cleanandmakein the checkout. - The tc32sim IOC must include
system.dbdand asyn serial support, andverify_autosave.shandverify_integrated.shload itsiocsh/tc32sim.iocshdevice setup. verify_caputlog.shuses the example IOC of steps 1 and 2 instead of tc32sim.
run_all.shruns eight scripts in turn. Each script prints itsPASS:andFAIL:lines and aPASS=<n> FAIL=<m>line, andrun_all.shprints aRESULTline after each script and anOVERALLline at the end:==== verify_linstat.sh ==== linStat: enabling module and rebuilding PASS: linStat host records present (ioctestlab-tc32sim:SYS_*) PASS: linStat host-net records present (ioctestlab-tc32sim:NET:HOST*) PASS: linStat proc records present (ioctestlab-tc32sim:IOC_*) PASS: linStat nic records present (ioctestlab-tc32sim:NET:lo*) PASS: linStat fs records present (ioctestlab-tc32sim:ROOT:*) -------------------- PASS=5 FAIL=0 RESULT verify_linstat.sh: PASS ==== verify_reccaster.sh ==== reccaster: enabling module and rebuilding PASS: reccaster record present (ioctestlab-tc32sim:State-Sts) PASS: reccaster record present (ioctestlab-tc32sim:Msg-I) -------------------- PASS=2 FAIL=0 RESULT verify_reccaster.sh: PASS ==== verify_iocstatsadmin.sh ==== iocStatsAdmin: enabling devIocStats and rebuilding PASS: iocStatsAdmin record present (ioctestlab-tc32sim:ACCESS) PASS: iocStatsAdmin record present (ioctestlab-tc32sim:HEARTBEAT) PASS: iocStatsAdmin record present (ioctestlab-tc32sim:STARTTOD) PASS: iocStatsAdmin record present (ioctestlab-tc32sim:UPTIME) -------------------- PASS=4 FAIL=0 RESULT verify_iocstatsadmin.sh: PASS ==== verify_autosave.sh ==== autosave: enabling module and rebuilding PASS: autosave pass1 retained across restart (all lines identical) PASS: autosave settings retained across restart (all lines identical) -------------------- PASS=2 FAIL=0 RESULT verify_autosave.sh: PASS ==== verify_ioclog.sh ==== iocLog: rebuilding (Base feature, no module macro) PASS: iocLog boot errlog received at server (proc=ioctestlab-tc32sim) -------------------- PASS=1 FAIL=0 RESULT verify_ioclog.sh: PASS ==== verify_serial.sh ==== serial: rebuilding and starting virtual PTYs PASS: serial params applied via config (baud 19200 on S1) PASS: serial skipped when SERIAL_ENABLE unset PASS: serial unreadable config reports error PASS: serial multiple ports get independent settings (S1 19200, S2 115200) -------------------- PASS=4 FAIL=0 RESULT verify_serial.sh: PASS ==== verify_caputlog.sh ==== caPutLog: orchestrating example IOC on isolated CA port PASS: caPutLog logged value change 0 -> 17 (new=17 old=0) PASS: caPutLog logged value change 17 -> 29 (new=29 old=17) PASS: caPutLog OPTION 0 suppressed the unchanged put (only the 29 change logged) -------------------- PASS=3 FAIL=0 RESULT verify_caputlog.sh: PASS ==== verify_integrated.sh ==== integrated: enabling all service modules and rebuilding Case A: aggregate boot of all services PASS: aggregate linStat host records present (:SYS_*) PASS: aggregate linStat proc records present (:IOC_*) PASS: aggregate linStat nic records present (:NET:lo*) PASS: aggregate linStat fs records present (:ROOT:*) PASS: aggregate reccaster records present (:State-Sts*) PASS: aggregate iocInit completed with all services loaded PASS: aggregate record names fully resolved (no macro cross-talk) PASS: aggregate loaded with no duplicate record collisions across services PASS: iocLog boot errlog reached server alongside other services PASS: caPutLog initialized in aggregate (coexists with autosave afterIocRunning) PASS: autosave afterIocRunning fired in aggregate (values_pass1.sav written) Case B: restart with autosave restore PASS: restart restored autosave values_pass1 set and reloaded all services Case C: minimal boot with optional services omitted PASS: minimal boot completed with optional NIC/FS/serial omitted -------------------- PASS=13 FAIL=0 RESULT verify_integrated.sh: PASS -------------------- OVERALL: PASSverify_integrated.shloads iocLog, serial, caPutLog, autosave, reccaster, and linStat in one IOC on port 7013 and leaves out iocStatsAdmin.run_all.shexits with 0 only when every script passes.
Verification
-
Count the passing cases in the summary file of step 4:
grep -c '"result": "Pass"' <evidence_dir>/summary.jsonAll six cases pass:
6
Uninstall and clean
Remove the installed tree and the cloned sources of one EPICS-env clone, and
keep its .local settings files for the next build.
Prerequisites
- The EPICS-env clone that built the tree, with the same
configure/CONFIG_SITE.local. The targets compute the tree path from it;make print-INSTALL_LOCATION_EPICSprints the path they act on. - Write access to the installed tree.
make uninstallremoves the tree as your user and does not usesudo.
-
Optional: to remove the installed files of one module, run its uninstall target before you remove the tree:
make uninstall.<module><module>is the module name without the version, such asasyn; theModulecolumn of Module pins and dependencies lists every name. The target runsmake uninstallin the module source tree, which reads the installed base, and leaves an empty module directory. Forstd,conf.stdsupplies the installed base path to the child IOC while retaining upstream cleanup recursion. Configure the module before cleanup if its child settings are absent or name another tree.make uninstall.modulesuninstalls every module and then removes its installation directory, preserving the installed base and source trees. -
Optional: to remove the build products and the installed files of one module and keep its sources, run its clean target before you remove the tree:
make distclean.<module>The target runs
make distcleanin the module source tree. The EPICSdistcleantarget also uninstalls, so the target leaves an empty module directory in the installed tree, asmake uninstall.<module>does.make clean.modulesruns this target for every module. It preserves tracked sources and user local settings. Upstream motor cleanup removes its generatedmodules/RELEASE.<host_arch>.local; the motor Makefile recreates that file when needed. -
Remove the installed tree:
make uninstallThe output names the path that the target removes:
Removing <install_location>/1.4.0/debian-13/7.0.10...<install_location>is the value ofINSTALL_LOCATION. The target removes the whole tree for the current release, operating system, and base version, including thevendordirectory, and leaves other trees under<install_location>in place. -
Remove the cloned sources:
make distcleanThe target removes
epics-base-src, every module source tree, andconfigure/MODULESGEN.mk. Make writesconfigure/MODULESGEN.mkagain on the next action invocation. Query-only invocations leave it absent and derive current module variables in memory. To remove only part of this set, runmake distclean.base,make distclean.modules, ormake distclean.modulesgen. -
Remove the version file that
make installwrote in the clone:make src_cleanThe target removes
site-template/.versions.
The .local files stay: configure/CONFIG_SITE.local,
configure/RELEASE.local, and the RELEASE.local and CONFIG_SITE.local
that make conf wrote at the repository top. A later
build reuses the first two, and make conf rewrites
the other two.
Verification
-
Check that the installed tree is gone:
LC_ALL=C make existThe output is:
No <install_location>/1.4.0/debian-13/7.0.10 -
Check that no source tree is left:
ls -d *-srcThe output is:
ls: cannot access '*-src': No such file or directory
Make targets by purpose
The top-level Makefile exposes every EPICS-env action as a make target. EPICS-env builds the Experimental Physics and Industrial Control System (EPICS) base and modules. Run
each target from the repository top as make <target>. A run with no target
prints the configuration variables, because vars is the default goal.
Targets that name a module use one of three spellings. The Module and Key
columns of Module pins and dependencies
list every module name and key:
<MODULE_KEY>is the upper-case key fromconfigure/RELEASE, such asASYNorSNCSEQ.<module>is the source directory name without its-srcsuffix, such asasyn,sequencer, orrecsync.conf.<module>targets use the names listed in Source configuration targets; a few differ from the source directory name, such asconf.sncseqforsequencer.
Some targets read a variable that you pass on the make command line. This
command audits only the asyn module:
make audit.module-deps MODULE=asyn
Variables set on the command line lists every such variable and its default.
Pipeline aggregate targets
| Target | Runs |
|---|---|
init | init.base and init.modules: clones EPICS base and every module at its pinned tag or commit |
patch | Every patch.*.apply target, in a fixed order |
patch.revert | Every patch.*.revert target, in the exact reverse order of patch |
conf | conf.base and conf.modules: writes the site files that point each source tree at its install location and dependencies |
build | conf.base, build.base, conf.modules, and build.modules: builds base and every module; module builds install as they complete |
install | install.base, install.modules, install.commoniocsh, and src_version |
symlinks | symlinks.modules: creates an unversioned link for each installed module |
distclean | distclean.base, distclean.modules, and distclean.modulesgen: removes the cloned source trees and configure/MODULESGEN.mk |
Source checkout targets
| Target | Effect |
|---|---|
init.base, clone.base | Clones EPICS base into epics-base-src, checks out its pinned tag, and initializes its submodules; skips an existing directory |
init.modules, clone.modules | Runs the clone target of every module |
<MODULE_KEY> | Clones one module and checks out its pinned tag or commit; skips an existing directory |
reconf.modules | Removes and regenerates configure/MODULESGEN.mk |
remove.genmk, clean.genmk | Removes every configure/*.mk file |
show.genmk | Prints every existing configure/*.mk file; if MODULESGEN.mk is absent, also prints its current derived configuration without creating it |
Upstream patch targets
| Target | Effect |
|---|---|
patch.base.pr.apply, patch.base.pr.revert | Applies the EPICS base patches patch/<base_version>-*.p0.patch in sorted order, or reverts them in the reverse order |
patch.pvxs.commit.apply, patch.pvxs.commit.revert | Applies the pvxs patches patch/<pvxs_version>-*.p0.patch in sorted order, or reverts them in the reverse order |
patch.base.apply, patch.base.revert | Applies or reverts patch/<base_version>.base.p0.patch when that file exists |
patch.<name>.apply, patch.<name>.revert | Applies or reverts one fixed module patch; <name> is mca, measComp, measComp.tc32, opcua, opcua.export, feed-core, QPC, or StreamDevice. The mca targets act only on macOS and do nothing on Linux |
patch.<name>.make, patch.base.make | Writes the current source changes of that module, or of EPICS base, as its patch file; patch.mca.make acts only on macOS |
Revert targets skip only confirmed unapplied patches and stop on conflicts, partial application, missing required inputs, or unresolved states. Completed reversals remain in effect after a later error. Optional empty patch sets and platform-inactive targets succeed without changes.
Source configuration targets
| Target | Effect |
|---|---|
conf.base | conf.base.site and conf.base.env |
conf.base.site | Removes epics-base-src/configure/CONFIG_SITE_ENV, which conf.base.env writes again, adds two linker lines to configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 when absent, and writes epics-base-src/configure/CONFIG_SITE.local: install location, linking with a run-time library search path (RUNPATH) relative to $ORIGIN, site version, and PYTHON = python3 |
conf.base.env | Writes epics-base-src/configure/CONFIG_SITE_ENV: time zone, Network Time Protocol (NTP) server, iocsh prompt and history, and input/output controller (IOC) log settings |
conf.modules | conf.release.modules, conf.modules.zero, and conf.modules.one |
conf.release.modules | Writes the RELEASE.local and CONFIG_SITE.local files that every module reads from the repository top |
conf.modules.zero | Runs the configuration targets of MCoreUtils, autosave, caPutLog, ether_ip, iocStats, pcas, pscdrv, retools, snmp, recsync, sncseq, sscan, opcua, pvxs, linStat, feed-core, QPC, and pyDevSup |
conf.modules.one | Runs the configuration targets of calc, asyn, modbus, lua, std, StreamDevice, busy, scaler, mca, measComp, motor, motorMotorSim, pmac, and rgamv2 |
conf.<module> | Configures one module; <module> is one of MCoreUtils, autosave, caPutLog, ether_ip, iocStats, pcas, pscdrv, retools, snmp, recsync, sncseq, sscan, opcua, pvxs, linStat, feed-core, QPC, pyDevSup, calc, asyn, modbus, lua, std, StreamDevice, busy, scaler, mca, measComp, motor, motorMotorSim, pmac, or rgamv2 |
conf.show, conf.base.show, conf.modules.show, conf.<module>.show | Prints the files the matching configuration target writes |
conf.gz.base, conf.gz.modules | Same as conf.base and conf.modules, and appends -g0 -gz=zlib to USR_CFLAGS, USR_CXXFLAGS, and USR_LDFLAGS |
user.conf | Copies configure_user/CONFIG_USER and configure_user/RULES_USER into ${HOME}/configure |
On Ubuntu 26, the individual configuration targets for sncseq, iocStats,
sscan, calc, busy, StreamDevice, lua, std, scaler, and mca
write USR_CFLAGS += -std=gnu17 in the module’s configure/CONFIG_SITE.local.
The same targets run under conf.modules and conf.gz.modules. Repeating a
configuration target rewrites the file with one copy of the flag; other
operating systems do not receive it.
conf.std also writes the installed base path into the std child IOC
iocs/stdTestIOC/configure/RELEASE.local. It replaces only EPICS_BASE
assignments in that file, preserves other settings, comments, and includes,
and avoids duplicate assignments on repetition or installation-root changes.
Build and install targets
| Target | Effect |
|---|---|
build.base | Builds EPICS base with four parallel jobs |
build.modules | Builds every module in dependency order, then runs install.modules |
build.<module> | Builds one module after the modules it depends on |
build.gz | Same as build, with the conf.gz.* configuration |
install.base | Installs EPICS base and copies scripts/setEpicsEnv.bash and scripts/resetEpicsEnv.bash to the top of the installed tree with mode 0644, backing up existing files |
install.modules, install.<module> | Installs every module, or one module |
install.commoniocsh | Copies commonIocsh/iocsh/*.iocsh to modules/commonIocsh/iocsh in the installed tree |
src_version | Writes the time it runs and the EPICS-env commit to .versions and installs that file at the top of the installed tree |
symlinks.modules | Creates every module link, then deletes dangling links under modules on Linux |
symlink.<module>, cleansymlink.<module> | Creates or removes the unversioned link of one module |
Verification and inspection targets
| Target | Effect |
|---|---|
audit.module-deps | Reports differences between each module’s declared and observed dependencies; reads MODULE, FORMAT, and PLATFORM |
check.module-deps | Same audit, and fails when an undeclared or unknown dependency exists |
audit.deps | Reports runpath defects in the installed executables and shared libraries without failing |
check.deps | Same scan, and fails on any finding |
audit.env | Reports each LD_LIBRARY_PATH entry under pvxs/bundle that the installed setEpicsEnv.bash adds |
check.env | Same check, and fails on a finding or when it cannot inspect the installed tree |
readelf.base, ldd.base, chrpath.base, readelf.runpath.base | Prints the dynamic section, resolved libraries, or runpath of the installed EPICS base files |
readelf.modules, ldd.modules, chrpath.modules | Same inspection for every installed module |
readelf.<module>, ldd.<module>, chrpath.<module> | Same inspection for one installed module |
exist | Prints the installed tree to depth LEVEL, default 2 |
exist.modules | Prints the installed modules directory to depth LEVEL plus 1 |
Clean and uninstall targets
| Target | Effect |
|---|---|
clean.base | Runs make clean in the EPICS base source tree |
clean.modules | Runs make distclean in every module source tree, which also empties each installed module directory; preserves sources and user local settings, except the upstream motor-generated host RELEASE file |
distclean.base | Removes the EPICS base source tree |
distclean.modules | Removes every module source tree |
distclean.modulesgen | Removes configure/MODULESGEN.mk |
uninstall | Removes the whole installed tree for the current release, operating system, and base version |
uninstall.modules | Runs make uninstall in every module, then removes every module install directory; preserves source trees and the installed base |
uninstall.<module> | Runs make uninstall in one module |
src_clean | Removes site-template/.versions |
Variable printing targets
An invocation is query-only when every selected goal is print-%, PRINT.%,
vars, env, default, ls.%, tree.%, cat.%, exist, exist.modules,
show.genmk, or a declared conf.*.show target, including conf.show.
Pattern query names require a nonempty variable name.
These invocations create no installation directory or generated file,
including with -n. File-display targets read their actual files and can
fail when those files are missing.
With no explicit goals, make classifies the effective .DEFAULT_GOAL; the
shipped value is vars. Command-line default overrides retain their target
behavior, and explicit goals take precedence. A mixed query/action list or
an unknown goal takes the action path, which probes the installation root
and can regenerate the cache. -n does not suppress those action-path effects.
| Target | Effect |
|---|---|
vars, env, default | Prints the configuration variables; FILTER=<prefix> limits the list to names that start with <prefix> |
print-<VARIABLE> | Prints the value of one variable |
PRINT.<VARIABLE> | Prints the value and the origin of one variable |
ls.<VARIABLE>, tree.<VARIABLE>, cat.<VARIABLE> | Runs ls, tree, or cat on the path a variable holds |
Continuous integration (CI) targets
| Target | Runs |
|---|---|
github | init, patch, vars, conf, build, and symlinks |
github.check | init, patch, vars, conf, check.module-deps, build, and symlinks |
Configuration variables and override files
EPICS-env reads its settings from make variables when it builds the Experimental Physics and Industrial Control System (EPICS) base and modules. The defaults live in
tracked files under configure/; a site changes them in untracked .local
files, so the tracked files stay unchanged.
Override files and read order
configure/CONFIG reads configure/RELEASE first and configure/CONFIG_SITE
second. Each file then reads its two override files, and a later file wins:
| Tracked file | Override files, in the order read |
|---|---|
configure/RELEASE | ../RELEASE.local (next to the repository), then configure/RELEASE.local |
configure/CONFIG_SITE | ../CONFIG_SITE.local (next to the repository), then configure/CONFIG_SITE.local |
Creating, editing, or removing either RELEASE.local override regenerates
configure/MODULESGEN.mk on the next action invocation when the effective
module triples change. Generated install directories follow the effective
SRC_VER_<MODULE_KEY> values without a manual reconf.modules step.
Query-only invocations use those effective values in memory and leave the
persistent cache unchanged, including when it is absent or stale.
The variables that configure/CONFIG_BASE defines with ?= accept a value
set in a CONFIG_SITE.local file, in the environment, or on the make command
line. LINKER_USE_RPATH uses :=, so only a value on the make command line
replaces it. Every EPICS base and module build receives LINKER_ORIGIN_ROOT
set to INSTALL_LOCATION_EPICS on its own command line, so no value set for
LINKER_ORIGIN_ROOT reaches a build.
conf.release.modules writes RELEASE.local and CONFIG_SITE.local at the
repository top for the modules to read. EPICS-env itself does not read those
two files, and each make conf overwrites them.
Install location and release
| Variable | Default | Set in | Meaning |
|---|---|---|---|
INSTALL_LOCATION | ${HOME}/epics | CONFIG_SITE.local | Root of every installed tree |
ENV_RELEASE_VERS | 1.4.0 | CONFIG_SITE.local | EPICS-env release number; the second level of the installed tree |
The installed tree for one host is
$(INSTALL_LOCATION)/$(ENV_RELEASE_VERS)/<os_id>-<os_version>/$(SRC_VER_BASE).
<os_id> and <os_version> are the operating system ID and VERSION_ID from /etc/os-release.
This command prints that path for the current host:
make print-INSTALL_LOCATION_EPICS
Actions probe INSTALL_LOCATION with mkdir -p. When the probe returns
1, make runs the
module build and install steps, the module links, and the commonIocsh
install through sudo. The EPICS base build and install and the .versions
install do not use sudo, so INSTALL_LOCATION must be writable by the
current user for a complete install.
A query computes SUDO_INFO without creating the directory. An existing
directory yields 0 when its parent path permits identifying it, even with
mode 000 on the final directory. An absent path requires search and create
access at the nearest existing ancestor; a blocked path or non-directory
yields 1. The result does not guarantee write access inside an existing
directory or predict quota, mount, or concurrent filesystem failures.
Source repositories and pins
| Variable | Default | Set in | Meaning |
|---|---|---|---|
SRC_URL_BASE | https://github.com/epics-base | RELEASE.local | Organization that hosts EPICS base |
SRC_URL_EPICSMODULES | https://github.com/epics-modules | RELEASE.local | Default organization for modules |
SRC_URL_<ORG> | One GitHub organization each | RELEASE.local | Organizations for modules hosted elsewhere: CHANNELFINDER, JEONGHANLEE, BRUNOSEIVAM, PSI, MOTOR, MD, BERKELEYLAB, PMAC |
SRC_NAME_BASE | epics-base | RELEASE.local | EPICS base repository name |
SRC_TAG_BASE | tags/R7.0.10 | RELEASE.local | EPICS base tag to check out |
SRC_VER_BASE | 7.0.10 | RELEASE.local | EPICS base version; the last level of the installed tree |
SRC_NAME_<MODULE_KEY> | Per module | RELEASE.local | Module repository name |
SRC_TAG_<MODULE_KEY> | Per module | RELEASE.local | Module tag or commit to check out |
SRC_VER_<MODULE_KEY> | Per module | RELEASE.local | Module version; part of the module install directory name |
Module pins and dependencies lists the value of every <MODULE_KEY>.
Vendor library locations
| Variable | Default | Set in | Meaning |
|---|---|---|---|
VENDOR_ULDAQ_PATH | /usr/local | RELEASE.local | Install prefix of the uldaq library that measComp links |
OPEN62541_PATH | /usr/local | RELEASE.local | Install prefix of the open62541 library that opcua links |
EPICS base site settings
conf.base.env writes these values into
epics-base-src/configure/CONFIG_SITE_ENV, so they become the defaults of
every input/output controller (IOC) built against the installed base.
EPICS base compiles CONFIG_SITE_ENV into its libCom library, so a changed
value takes effect only after make conf.base, make build.base, and
make install.base run again.
| Variable | Default | Meaning |
|---|---|---|
EPICS_TZ | "PST8PDT,M3.2.0/2,M11.1.0/2" | Time zone rule |
EPICS_TS_NTP_INET | time.google.com | Network Time Protocol (NTP) server |
IOCSH_PS1 | "<base_version> > " | iocsh prompt |
IOCSH_HISTSIZE | 50 | Lines of iocsh command history |
IOCSH_HISTEDIT_DISABLE | empty | Disables line editing in iocsh when set |
EPICS_IOC_LOG_INET | empty | IOC log server address |
EPICS_IOC_LOG_FILE_NAME | empty | IOC log file path |
EPICS_IOC_LOG_FILE_COMMAND | empty | Command that returns a log file path after SIGHUP |
EPICS_IOC_LOG_FILE_LIMIT | 1000000 | IOC log file size limit |
conf.base.site writes these values into
epics-base-src/configure/CONFIG_SITE.local:
| Variable | Default | Meaning |
|---|---|---|
CROSS_COMPILER_TARGET_ARCHS | empty | Cross target architectures for EPICS base |
EPICS_SITE_VERSION | "github.com/jeonghanlee/EPICS-env" | Site version string built into EPICS base |
EPICS_VCS_VERSION | "EPICS-env-<base_version>-<git_describe>" | Version string written as GENVERSIONDEFAULT |
Variables set on the command line
| Variable | Default | Used by | Meaning |
|---|---|---|---|
VERBOSE | unset | Every target | Prints recipe commands when set |
DEBUG_SHELL | unset | Every target | Runs recipes under /bin/sh -x when set |
FILTER | 1 (no filter) | vars | Prints only variables whose names start with the given prefix |
LEVEL | 2 | exist, exist.modules, tree.<VARIABLE> | Tree depth |
LSOPTS | unset | ls.<VARIABLE> | Options passed to ls |
MODULE | empty (all) | audit.module-deps, check.module-deps | Audits one module |
FORMAT | text | audit.module-deps, check.module-deps | Report format: text or json |
PLATFORM | output of uname -s | audit.module-deps, check.module-deps | With Linux, dependencies found under a module’s os/Linux, os/posix, and os/default directories count as required; with any other value they count as optional |
Module pins and dependencies
configure/RELEASE pins the Experimental Physics and Industrial Control System
(EPICS) base and every module to one tag or commit.
The values below are the ones make computes from the tracked
configure/RELEASE and configure/CONFIG_MODS, with no override file.
When a RELEASE.local file overrides a pin, this command prints the value in
effect for one module in your checkout:
make print-SRC_TAG_<MODULE_KEY>
<MODULE_KEY> is the Key column below, such as ASYN.
EPICS base repository pin
| Repository | Pin | Version | Install directory |
|---|---|---|---|
| https://github.com/epics-base/epics-base | tags/R7.0.10 | 7.0.10 | base |
Module repositories and pins
Module is the name the module’s make targets use, such as build.asyn.
Key is the upper-case name of its SRC_* variables in configure/RELEASE.
Install directory is the directory under modules/ in the installed tree;
make symlinks adds a link without the version suffix. Depends on lists
the modules that build first. MCoreUtils is part of the set only on Linux.
| Module | Key | Repository | Pin | Version | Install directory | Depends on |
|---|---|---|---|---|---|---|
| asyn | ASYN | https://github.com/epics-modules/asyn | tags/R4-46 | 4.46.0 | asyn-4.46.0 | calc, sequencer, sscan |
| autosave | AUTOSAVE | https://github.com/epics-modules/autosave | tags/R6-0 | 6.0.0 | autosave-6.0.0 | EPICS base only |
| busy | BUSY | https://github.com/epics-modules/busy | 2dfe92d | 2dfe92d | busy-2dfe92d | asyn, autosave |
| calc | CALC | https://github.com/epics-modules/calc | 4217e83 | 4217e83 | calc-4217e83 | sequencer, sscan |
| caPutLog | CAPUTLOG | https://github.com/epics-modules/caPutLog | dafb0b2 | dafb0b2 | caPutLog-dafb0b2 | EPICS base only |
| ether_ip | ETHERIP | https://github.com/epics-modules/ether_ip | tags/ether_ip-3-10 | 3.10.0 | ether_ip-3.10.0 | EPICS base only |
| feed-core | FEEDCORE | https://github.com/BerkeleyLab/feed-core | 0472d88 | 0472d88 | feed-core-0472d88 | EPICS base only |
| iocStats | IOCSTATS | https://github.com/epics-modules/iocStats | tags/4.0.1 | 4.0.1 | iocStats-4.0.1 | EPICS base only |
| linStat | LINSTAT | https://github.com/mdavidsaver/linStat | tags/1.2.1 | 1.2.1 | linStat-1.2.1 | EPICS base only |
| lua | LUA | https://github.com/epics-modules/lua | 17475b5 | 17475b5 | lua-17475b5 | asyn |
| mca | MCA | https://github.com/epics-modules/mca | 687d563 | 687d563 | mca-687d563 | asyn, autosave, busy, calc, scaler, sequencer, sscan, std |
| MCoreUtils | MCOREUTILS | https://github.com/epics-modules/MCoreUtils | a86e5ed | a86e5ed | MCoreUtils-a86e5ed | EPICS base only |
| measComp | MEASCOMP | https://github.com/epics-modules/measComp | c38974e | c38974e | measComp-c38974e | asyn, autosave, busy, calc, mca, scaler, sequencer, sscan, std |
| modbus | MODBUS | https://github.com/epics-modules/modbus | tags/R3-4 | 3.4.0 | modbus-3.4.0 | asyn |
| motor | MOTOR | https://github.com/epics-modules/motor | 285f44d | 285f44d | motor-285f44d | asyn, busy, lua, modbus, sequencer |
| motorMotorSim | MOTORSIM | https://github.com/epics-motor/motorMotorSim | tags/R1-3 | 1.3.0 | motorMotorSim-1.3.0 | asyn, motor |
| opcua | OPCUA | https://github.com/epics-modules/opcua | tags/v0.11.2 | 0.11.2 | opcua-0.11.2 | EPICS base only |
| pcas | PCAS | https://github.com/epics-modules/pcas | e075fd4 | e075fd4 | pcas-e075fd4 | EPICS base only |
| pmac | PMAC | https://github.com/DiamondLightSource/pmac | 2-7-9 | 2.7.9 | pmac-2.7.9 | asyn, busy, calc, motor |
| pscdrv | PSCDRV | https://github.com/mdavidsaver/pscdrv | 276daca | 276daca | pscdrv-276daca | EPICS base only |
| pvxs | PVXS | https://github.com/epics-base/pvxs | tags/1.5.2 | 1.5.2 | pvxs-1.5.2 | EPICS base only |
| pyDevSup | PYDEVSUP | https://github.com/epics-modules/pyDevSup | 4527ed0 | 4527ed0 | pyDevSup-4527ed0 | EPICS base only |
| QPC | QPC | https://github.com/jeonghanlee/QPC | 913fad4 | 913fad4 | QPC-913fad4 | asyn |
| recsync | RECSYNC | https://github.com/ChannelFinder/recsync | 9834b94 | 9834b94 | recsync-9834b94 | EPICS base only |
| retools | RETOOLS | https://github.com/brunoseivam/retools | 5ada1e1 | 5ada1e1 | retools-5ada1e1 | EPICS base only |
| rgamv2 | RGAMV2 | https://github.com/jeonghanlee/rgamv2 | 27fc633 | 27fc633 | rgamv2-27fc633 | asyn |
| scaler | SCALER | https://github.com/epics-modules/scaler | beb5521 | beb5521 | scaler-beb5521 | asyn, autosave |
| sequencer | SNCSEQ | https://github.com/epics-modules/sequencer | tags/R2-2-9 | 2.2.9 | seq-2.2.9 | EPICS base only |
| snmp | SNMP | https://github.com/jeonghanlee/snmp | tags/v1.1.0.4ja | 1.1.0.4ja | snmp-1.1.0.4ja | EPICS base only |
| sscan | SSCAN | https://github.com/epics-modules/sscan | e13699e | e13699e | sscan-e13699e | sequencer |
| std | STD | https://github.com/epics-modules/std | 5f2e442 | 5f2e442 | std-5f2e442 | asyn, sequencer |
| StreamDevice | STREAM | https://github.com/paulscherrerinstitute/StreamDevice | tags/2.8.26 | 2.8.26 | StreamDevice-2.8.26 | asyn, calc |
Common iocsh fragment macros
Each common iocsh fragment in modules/commonIocsh/iocsh takes its settings
as macros in the second argument of iocshLoad. An input/output controller
(IOC) of the Experimental Physics and Industrial Control System (EPICS) sets
IOCSH_TOP to modules/commonIocsh and loads a fragment as
iocshLoad("$(IOCSH_TOP)/iocsh/<fragment>.iocsh", "<macros>").
A default in the tables below applies when the IOC does not pass the macro.
Common iocsh fragments explains the
macro sources and the load order.
Module and database definition requirements
Each fragment needs support that the IOC links and registers. Module top macro is the configure/RELEASE macro that the fragment reads through
envPaths. DBD files are the database definition (DBD) files that the IOC
includes, and Library is the library it links. The libCom library of
EPICS base registers the iocsh commands that iocLog.iocsh calls, so that
fragment needs no DBD file:
| Fragment | Module top macro | DBD files | Library |
|---|---|---|---|
autosave.iocsh | AUTOSAVE | asSupport.dbd, system.dbd | autosave |
caPutLog.iocsh | none | caPutLog.dbd | caPutLog |
iocLog.iocsh | none | none | Com of EPICS base |
iocStatsAdmin.iocsh | devIocStats | devIocStats.dbd | devIocStats |
linStat.iocsh and linStat*.iocsh | LINSTAT | linStat.dbd | linStat |
reccaster.iocsh | RECCASTER | reccaster.dbd | reccaster |
serial.iocsh | none | none | none |
setSerialParams.iocsh | none | asyn.dbd, drvAsynSerialPort.dbd | asyn |
Macros for autosave.iocsh
autosave.iocsh sets the save and request file paths to
$(AS_TOP)/$(IOC)/save and $(AS_TOP)/$(IOC)/req and creates both
directories. It loads $(AUTOSAVE)/db/save_restoreStatus.db with the prefix
$(IOC)-as:. The first restore pass reads the settings and pass 0 files,
and the second reads the settings and pass 1 files. After iocInit, it
writes a request file for each group from the autosaveFields,
autosaveFields_pass0, and autosaveFields_pass1 info tags and starts a
monitor set for each.
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Status record prefix $(IOC)-as: and the per-IOC directory under AS_TOP |
AS_TOP | Required | Writable root directory of the save and request files |
AUTOSAVE | Required; from envPaths | Install directory of the autosave module |
INCOMPLETE | 1 | Restores and saves a group even when some of its values are missing |
CA_RECONNECT | 1 | Retries the connection to a process variable whose first connection failed |
DATED_BACKUP | 1 | Writes dated backup files |
NUM_SEQ | 3 | Number of sequenced backup files |
SEQ_PERIOD | 300 | Seconds between sequenced backups |
SETTINGS_FILES | settings | Base name of the settings group files |
VALUES_FILES_PASS0 | values_pass0 | Base name of the pass 0 value group files |
VALUES_FILES_PASS1 | values_pass1 | Base name of the pass 1 value group files |
SETTINGS_PERIOD | 5 | Seconds between saves of the settings group |
VALUES_PASS0_PERIOD | 5 | Seconds between saves of the pass 0 value group |
VALUES_PASS1_PERIOD | 10 | Seconds between saves of the pass 1 value group |
DEAD_SECONDS | 5 | DEAD_SECONDS value of save_restoreStatus.db |
Macros for caPutLog.iocsh
caPutLog.iocsh registers
caPutLogInit('$(LOG_INET):$(LOG_INET_PORT)', $(OPTION)) with
afterIocRunning, so the logger starts after iocInit completes. The IOC
supplies an access security configuration file that marks writes with
TRAPWRITE.
| Macro | Required or default | Meaning |
|---|---|---|
LOG_INET | Required | Address of the log server that receives put records |
LOG_INET_PORT | 7004 | Transmission Control Protocol (TCP) port of the log server |
OPTION | 0 | -1 disables logging; 0 logs value changes; 1 logs every put, including an unchanged value; 2 logs every put without combining bursts on one field |
Macros for iocLog.iocsh
iocLog.iocsh sets the environment variables EPICS_IOC_LOG_INET,
EPICS_IOC_LOG_PORT, and iocLogDisable. It then sets the message prefix
to fac=$(FACNAME) proc=$(IOC) and calls iocLogInit.
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Name after proc= in each message |
LOG_INET | Required | Address of the log server; written to EPICS_IOC_LOG_INET |
LOG_INET_PORT | 7004 | TCP port of the log server; written to EPICS_IOC_LOG_PORT |
LOGDISABLE | 0 | Written to the environment variable iocLogDisable; EPICS base reads its iocLogDisable program variable instead, so this value does not turn logging off |
FACNAME | empty | Facility name after fac= in each message |
Macros for iocStatsAdmin.iocsh
iocStatsAdmin.iocsh loads $(devIocStats)/db/iocAdminSoft.db with
IOC=$(IOC). It creates records such as $(IOC):ACCESS,
$(IOC):HEARTBEAT, $(IOC):STARTTOD, and $(IOC):UPTIME.
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Record name prefix; at most 21 characters |
devIocStats | Required; from envPaths | Install directory of the iocStats module |
Macros for linStat.iocsh
linStat.iocsh loads linStatHost.iocsh and linStatProc.iocsh from
$(IOCSH_TOP)/iocsh with IOC=$(IOC). It also loads linStatNIC.iocsh
when NICENABLE is empty, and linStatFS.iocsh when FSENABLE is empty.
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Record name prefix, passed to every sub-fragment |
LINSTAT | Required; from envPaths | Install directory of the linStat module |
IOCSH_TOP | Required | Directory that holds the iocsh directory of the fragments, such as <installed_tree>/modules/commonIocsh; the sub-fragments load from $(IOCSH_TOP)/iocsh |
NICENABLE | #-- | Set to empty to load linStatNIC.iocsh for one network interface |
NIC | empty | Network interface name, such as eth0; required when NICENABLE is empty |
FSENABLE | #-- | Set to empty to load linStatFS.iocsh for one filesystem |
FSID | empty | Unique tag of the filesystem, such as ROOT; required when FSENABLE is empty |
DIR | empty | Mount path of the filesystem, such as /; required when FSENABLE is empty |
Macros for linStatHost.iocsh
linStatHost.iocsh loads $(LINSTAT)/db/linStatHost.db with IOC=$(IOC).
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Record name prefix |
LINSTAT | Required; from envPaths | Install directory of the linStat module |
Macros for linStatProc.iocsh
linStatProc.iocsh loads $(LINSTAT)/db/linStatProc.db with IOC=$(IOC).
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Record name prefix |
LINSTAT | Required; from envPaths | Install directory of the linStat module |
Macros for linStatNIC.iocsh
linStatNIC.iocsh loads $(LINSTAT)/db/linStatNIC.db with IOC=$(IOC) and
NIC=$(NIC). The records are named $(IOC):NET:$(NIC):*.
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Record name prefix |
NIC | Required | Network interface name, such as eth0 |
LINSTAT | Required; from envPaths | Install directory of the linStat module |
Macros for linStatFS.iocsh
linStatFS.iocsh loads $(LINSTAT)/db/linStatFS.db with
P=$(IOC):$(FSID), DIR=$(DIR), and PERIOD=$(PERIOD). The records are
named $(IOC):$(FSID):*.
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Record name prefix |
FSID | Required | Unique tag of the filesystem, such as ROOT |
DIR | Required | Mount path of the filesystem, such as / |
PERIOD | 10 | Scan period in seconds |
LINSTAT | Required; from envPaths | Install directory of the linStat module |
Macros for reccaster.iocsh
reccaster.iocsh sets the reccaster variables reccastTimeout and
reccastMaxHoldoff. It loads $(RECCASTER)/db/reccaster.db with
P=$(IOC):, which creates $(IOC):State-Sts and $(IOC):Msg-I.
| Macro | Required or default | Meaning |
|---|---|---|
IOC | Required | Record name prefix |
RECCASTER | Required; from envPaths | Install directory of the recsync module |
RECCAST_TIMEOUT | 20.0 | Client timeout in seconds; written to reccastTimeout |
RECCAST_MAX_HOLDOFF | 10.0 | Maximum holdoff in seconds; written to reccastMaxHoldoff |
Macros for serial.iocsh
serial.iocsh loads the file named in SERIAL_CONFIG with iocshLoad when
SERIAL_ENABLE is empty. That file belongs to the IOC and loads
setSerialParams.iocsh once for each serial port. When SERIAL_ENABLE is
not passed, the line is a comment, and iocsh prints that SERIAL_CONFIG is
undefined. A SERIAL_CONFIG path that cannot be read makes iocsh print
Can't open <path>, and the startup script continues.
| Macro | Required or default | Meaning |
|---|---|---|
SERIAL_ENABLE | #-- | Set to empty to load the serial configuration file |
SERIAL_CONFIG | Required when SERIAL_ENABLE is empty | Path of the serial configuration file |
Macros for setSerialParams.iocsh
setSerialParams.iocsh calls asynSetOption for baud, bits, stop,
and parity on one asyn port, with address -1. It does not set hardware
or software flow control.
| Macro | Required or default | Meaning |
|---|---|---|
PORT | Required | asyn port name that drvAsynSerialPortConfigure created |
BAUD | 9600 | Baud rate |
BITS | 8 | Data bits: 5, 6, 7, or 8 |
STOP | 1 | Stop bits: 1 or 2 |
PARITY | none | Parity: none, odd, or even |
Tools and scripts reference
EPICS-env builds the Experimental Physics and Industrial Control System (EPICS)
base and modules with make. The tools/ and scripts/ directories hold the
programs around that build: checks of the installed tree, dependency and pin
surveys, process variable (PV) helpers, and scripts that set up a shell for an
installed tree. You run each Bash tool with bash. This command prints the
usage of one tool:
bash tools/check_env.bash --help
audit_module_deps.bash without --top and check_deps.bash without an
installed tree argument read make variables in the current directory; run them
from the repository top. gen_dep_graph.bash, prep-vendors.bash,
update-release.bash, and verify_fix_build.bash locate the checkout from
their own path.
Programs in the tools directory
Placeholders in the Interface column:
<installed_tree>is the installed tree for one host, the value ofmake print-INSTALL_LOCATION_EPICS.<repo>is the top directory of an EPICS-env checkout.<name>after--moduleis the lower-case module name, such asasyn.<pv_list>is a file with one PV name per line.
| Tool | Purpose | Interface | Exit status | Called by |
|---|---|---|---|---|
audit_module_deps.bash | Compares each module’s declared dependencies (<module>_DEPS) with the dependencies found in its source tree: configure/RELEASE.local, Makefiles, .dbd, database, startup, and C or C++ source files | --top <repo>, --module <name>, --format text or json, --platform <name>, --strict, --help | 0 report printed with no strict failure; 1 argument error or no module matched; 2 with --strict when an undeclared-observed or unknown finding exists | audit.module-deps, check.module-deps, and every OS workflow: through github.check on Debian 12, Debian 13, and Rocky 8, and directly on Rocky 10 and both Ubuntu workflows |
check_deps.bash | Scans each canonical installed executable once and scans the shared libraries of base, modules, and vendor/lib. It reports each file that has a run-time search path (RPATH) entry, each absolute RPATH or run-time library search path (RUNPATH) entry outside system library directories, and each shared library that needs a tree library without $ORIGIN in its RPATH or RUNPATH | -v or --verbose, --report-only, then an optional <installed_tree>; without a positional argument the tool reads INSTALL_LOCATION_EPICS from make in the current directory | 0 no finding, or any finding with --report-only; 1 unknown option; 2 any finding without --report-only, invalid installed-tree input, unresolved executable path, or readelf not found | audit.deps (with --report-only), check.deps, every operating system (OS) workflow, and prep-vendors.bash check-deps |
check_env.bash | Sources the installed setEpicsEnv.bash in a shell started with env -i, and reports each LD_LIBRARY_PATH entry that contains a pvxs/bundle path component | --epics <installed_tree> (required), --strict, --require-run, --help | 0 no finding, a finding without --strict, or check skipped; 1 argument error; 2 with --strict when a finding exists; 3 with --require-run when the script is absent, EPICS_MODULES or EPICS_HOST_ARCH stays empty, or LD_LIBRARY_PATH stays empty | audit.env, check.env (with --strict --require-run), and every OS workflow |
gen_dep_graph.bash | Writes a Graphviz image of the module dependency graph from the <module>_DEPS lines, labeled with the commit and its date; needs the dot command | -f <file> (default configure/CONFIG_MODS_DEPS), -o <file> (default epics_deps.png; the extension sets the image format), -v, -h | 0 image written or help; 1 rendering error, missing input file or Graphviz, or unknown option; 2 a missing option value, with a diagnostic | No target or workflow |
prep-vendors.bash | Clones uldaq-env and open62541-env into ${HOME}/.vendor_temp_folder, builds and installs both into <installed_tree>/vendor, and builds EPICS-env against them | One command: init, prep-uldaq, prep-open62541, prep-vendors, epics-env, epics-build <make_target>, show-env, check-deps followed by check_deps.bash options, all, OS, or help | 0 success or help; 1 unknown command, missing argument, backup failure, refused replacement, or EOF at confirmation; otherwise the status of the failed step | No target or workflow |
pv_snapshot.bash | Captures PV values with caget into a snapshot file, and compares two snapshots PV by PV as SAME, WITHIN, DIFF, MISSING, or DISCONN | capture -l <pv_list> -o <snapshot> [-w <seconds>]; compare [-t <tolerance>] <before> <after>; --help | 0 no difference beyond the tolerance; 1 a DIFF, MISSING, or DISCONN PV; 2 usage or run-time errors, including a missing option value with a diagnostic | No target or workflow; verify_fix_build.bash names it in the manual steps it prints |
pvs_gets.bash | Reads every PV of a list, sorted, with caget or pvget, once or in a loop | -l <pv_list> (required), -f <regex>, -r <field>, -w <seconds>, -c, -n, -7 | 0 run finished; 1 usage error or no -l; 2 the selected client is missing or not executable, in single and watch modes; -w runs until interrupted when the client is available | No target or workflow |
revert_patch.bash | Classifies a whole patch, reverses a confirmed applied patch, and skips a confirmed unapplied patch | [--no-backup-if-mismatch] [--prerequisites] <source> <patch> [<ordered_patches>...]; the ordered list includes the selected patch | 0 reversed or confirmed unapplied; 2 invalid or unresolved inputs; otherwise a failed command status | Every active patch.*.revert target |
tc32-expansion-query.cpp | C++ source, built separately, of a program that reports whether a measComp TC-32 or E-TC32 has the EXP-32 expansion attached, through the installed uldaq library | One argument: the unique ID that the input/output controller (IOC) passes to the measComp driver | 0 query succeeded; 2 usage; 3 no device or more than one device matches; 4 a uldaq call failed | No target or workflow |
update-release.bash | Surveys every module pin in configure/RELEASE against its remote: tag pins against the latest release tag, commit pins against the branch head | [-v] check, [-v] update, or help; reads GITHUB_TOKEN when set | check: 0 survey complete, 1 a module unreachable or without a repository URL, 2 a module has no release tag; update: 0 run finished or choice 4 taken, 1 configure/RELEASE not found; help: 0; usage error: 2 | No target or workflow |
verify_fix_build.bash | Builds a fixed module copy and a consumer IOC copy against the installed tree named by EPICS_BASE, then checks their RPATH or RUNPATH entries, their resolved libraries, and that nothing under the tree was written | <module_dir> <ioc_dir>, after setEpicsEnv.bash of the tree is sourced; --help | 0 all checks passed; 1 a precondition, build, or check failed, or a dependency confirmation was refused or had no terminal; 2 wrong argument count | No target or workflow |
Behavior details of the tools
-
audit_module_deps.bashreads the module list, each<module>_DEPS, and theAUDIT_*token lists throughmake print-<VARIABLE>in the directory that--topnames.--platformdefaults to the output ofuname -s. -
check_deps.bashlooks only inbin/linux-x86_64andlib/linux-x86_64underbaseand each module, and invendor/lib. An RPATH or RUNPATH entry that starts with/usr/lib, such as/usr/lib64or/usr/lib/x86_64-linux-gnu, prints a note and does not count as a finding. -
check_deps.bashresolves executable paths withrealpath -eand removes duplicate canonical paths before callingreadelf. Empty or non-directory tree paths and failed make lookups exit 2 with guidance to setINSTALL_LOCATION_EPICSor pass a valid<installed_tree>, including with--report-only. -
revert_patch.bashruns noninteractive GNU patch dry-runs before changing sources. For supported single-file, single-hunk patches, it checks that changed lines match the uniquely named static C function in the hunk header. Conflicting, partial, missing-input, and unresolved states fail with the source and patch identified. Both directions remaining valid is unresolved. -
Revert suppresses mismatch backups by default;
--no-backup-if-mismatchaccepts the same behavior explicitly. Existing target backups are preserved. -
Revert classification can copy real patch-owned source files into a private workspace. With
--prerequisites, it supplies context from earlier patches there. For supported static C functions, a private patch adjusts only hunk search positions; GNU patch must match the contents inside the named function. Classification leaves the original source and shipped patches unchanged. Successful classification removes that workspace; failure retains it and prints its path. Fixed module targets pass no prerequisite list. -
pvs_gets.bashtreats-fas a regular expression.-rappends.<field>to each PV name.-7reads withpvgetinstead ofcaget.-csetsEPICS_CA_ADDR_LISTto the host’s Internet Protocol version 4 (IPv4) address andEPICS_CA_AUTO_ADDR_LISTtoYES, or toNOtogether with-n. -
update-release.bash updateoffers four choices for each module whose pin differs from the latest release tag or branch head: keep the pin (the default), take the latest, enter a value, or exit. Choice 4 removesconfigure/RELEASE.newand exits 0 without writingconfigure/RELEASE. -
After the last module,
update-release.bash updateshows the difference and asks before it writesconfigure/RELEASE. It copies the replaced file toconfigure/RELEASE.bak. -
verify_fix_build.bashneedsconfigure/MODULESGEN.mkin the checkout that holds it. It writesconfigure/RELEASE.localandconfigure/CONFIG_SITE.localin both copies. It keepsmodule-build.log,ioc-build.log, and the empty marker file.startedin the parent directory of<module_dir>. It writes.startedbefore the builds and fails when a file under the installed tree changes after that point. -
When the module dependencies of the checkout differ from those of the installed module,
verify_fix_build.bashprints the difference and asks for confirmation on/dev/tty. Without a terminal, or on any answer other thanyorY, it exits 1. -
tc32-expansion-query.cpphas no make target. From the repository top, these commands build it against the uldaq library of an installed tree:VENDOR=<installed_tree>/vendor g++ -c -I"${VENDOR}/include" tools/tc32-expansion-query.cpp g++ -o tc32-expansion-query tc32-expansion-query.o -L"${VENDOR}/lib" -Wl,-rpath,"${VENDOR}/lib" -luldaq<installed_tree>is the installed tree, as in the table above. The second command compiles the source, and the third links the program in the current directory. The-Wl,-rpathoption records the vendor library directory in the program as its RUNPATH, so it findslibuldaq.soat run time. The program andtc32-expansion-query.ostay in the repository top, and Git does not ignore them. -
prep-vendors.bash initempties${HOME}/.vendor_temp_folderand writesconfigure/CONFIG_SITE.local. Vendor preparation writes that file in each vendor checkout.epics-envandepics-buildrewriteconfigure/RELEASE.localwith vendor paths and use the Network Time Protocol (NTP) default,time.google.com, fromCONFIG_BASE. -
Before replacing an existing local configuration,
prep-vendors.bashasks for confirmation when stdin is a terminal. It proceeds automatically when stdin is not a terminal. Both modes preserve a byte-identical backup as<file>.bak.<YYYYMMDDTHHMMSSZ>, using UTC and a numeric suffix on collision. Earlier backups remain unchanged. Backup failure, refusal, or EOF preserves the original and stops the invocation with status 1 before later build steps. -
The vendor builds use
conf.rocky10on Rocky 10,conf.rocky8on Rocky 8, Red Hat Enterprise Linux (RHEL), CentOS, and Fedora, andconfelsewhere.allrunsinit,prep-vendors, andepics-envwithout a version argument.
Shell scripts in the scripts directory
| Script | Use | Effect | Installed |
|---|---|---|---|
setEpicsEnv.bash | Sourced as [<fallback_arch>] [disable] | Resolves its tree and architecture before replacing known environment paths, then exports EPICS_PATH, EPICS_BASE, EPICS_MODULES, and EPICS_HOST_ARCH. Prepends modules/pmac/bin/<arch>, modules/pvxs/bin/<arch>, and base/bin/<arch> to PATH, and base/lib/<arch> to LD_LIBRARY_PATH. Returns 0 on success, 2 for invalid arguments, or 1 when tree or architecture resolution fails | Yes: install.base copies it to the top of the installed tree with mode 0644 |
resetEpicsEnv.bash | Sourced | Removes known base, pvxs, pmac, and legacy extension executable entries and the base library entry, then unsets EPICS_PATH, EPICS_BASE, EPICS_HOST_ARCH, EPICS_MODULES, and EPICS_EXTENSIONS. Returns 0, including when base is absent or reset is repeated | Yes: install.base copies it to the top of the installed tree with mode 0644 |
selectEpicsEnv.bash | Sourced, with optional arguments <epics_top> and <base_version> | Sources <epics_top>/epics/<os_id>/<os_version>/<base_version>/setEpicsEnv.bash; <epics_top> defaults to ${HOME} and <base_version> to 7.0.4.1 | No |
build_epics.bash | Executed with bash, with an optional argument <prefix> | Writes configure/CONFIG_SITE.local with INSTALL_LOCATION:=<prefix>/epics, so INSTALL_LOCATION becomes <prefix>/epics. Runs init, patch, conf, build, install, and symlinks, and creates <prefix>/epics/R<base_version> with a copy of setEpicsEnv.bash and base and modules links. It then clones EPICS-env-support into the current directory and builds it against the installed base | No |
install_apps.bash | Executed with bash, with an optional argument <prefix> | Installs PMD 7.22.0 into <prefix>/apps/pmd; on Rocky it also builds splint and installs ShellCheck under <prefix>/apps. Writes <prefix>/setEnv, which sources <prefix>/epics/R<base_version>/setEpicsEnv.bash from build_epics.bash | No |
<prefix> defaults to /usr/local. <base_version> is an EPICS base version,
such as 7.0.10; build_epics.bash and install_apps.bash take it from
make print-PATH_NAME_EPICSVERS. <os_id> and <os_version> are the ID
and VERSION_ID values in /etc/os-release. <arch> is the value of
EPICS_HOST_ARCH. setEpicsEnv.bash takes EPICS_HOST_ARCH from
EpicsHostArch.pl under base/startup or base/lib/perl, or from
base/startup/EpicsHostArch. It uses its argument as the architecture only
when none of these files exists or perl is missing. A discovery command
failure returns 1 before replacement. The argument disable suppresses the
summary without becoming the architecture. Setup and reset are safe under
set -u, compare complete path fields, and preserve independently configured
CA settings. Select among installed versions by directly sourcing each
chosen tree’s setup script. The selector’s legacy path differs from make’s
<install_location>/<release>/<os_id>-<os_version>/<base_version> layout.
User configuration files for make
make user.conf copies the two files in configure_user/ into
${HOME}/configure, keeping a backup of any file it replaces. No EPICS-env
make file reads the copies in ${HOME}/configure.
| File | Content |
|---|---|
CONFIG_USER | VARS_EXCLUDES entries that hide shell, terminal, desktop, and tool variables from the variable listing |
RULES_USER | The variable printing targets vars, env, print-<VARIABLE>, PRINT.<VARIABLE>, ls.<VARIABLE>, tree.<VARIABLE>, and cat.<VARIABLE>, the exist target, and the defaults QUIET=@, LEVEL=2, FILTER=1, and LSOPTS="-lta" |
Build version record in site-template
make src_version writes site-template/.versions, holding the time it runs
and the EPICS-env commit, and installs that file at the top of the installed
tree. make src_clean removes site-template/.versions.
Supported platforms and CI
EPICS-env builds the Experimental Physics and Industrial Control System (EPICS)
base and modules on six Linux operating systems (OS). Each OS has one
continuous integration (CI) workflow in .github/workflows/ that builds and
checks the whole environment in a container. Two more workflows lint the Bash
sources and publish this book. Every workflow runs on a GitHub Actions
ubuntu-latest runner.
Supported operating systems and containers
The supported OS list is the set of OS workflows. README.md shows one status
badge for each of them and one for the Linter Run workflow.
| OS | Workflow file | Workflow name | Container image |
|---|---|---|---|
| Debian 12 | debian12.yml | Debian 12 | debian:bookworm-slim |
| Debian 13 | debian13.yml | Debian 13 | debian:trixie-slim |
| Rocky Linux 8 | rocky8.yml | Rocky 8 | rockylinux/rockylinux:8 |
| Rocky Linux 10 | rocky10.yml | Rocky 10 | rockylinux/rockylinux:10 |
| Ubuntu 24.04 | ubuntu24.yml | Ubuntu 24.04 | ubuntu:24.04 |
| Ubuntu 26.04 | ubuntu26.yml | Ubuntu 26.04 | ubuntu:26.04 |
Package setup in the OS workflows
Each OS workflow prepares its container in the Install required packages
step:
- It installs
git,make,sudo,bash,wget, andunzipwithaptordnf. The Rocky 8 workflow runsdnf updatefirst and also installstree. - It clones
pkg_automationfromhttps://github.com/jeonghanleeand runspkg_automation.bash -yto install the build dependencies. - The Rocky 8 workflow then installs
python3-pipandlibusbx-devel, and installsnumpyandnosewithpip3. - The Ubuntu workflows set the time zone to
America/Los_Angelesand exportLC_CTYPEandLC_ALLasC.UTF-8.
Vendor library build in the OS workflows
Each OS workflow builds the uldaq and open62541 libraries before EPICS-env.
The vendor directory is the vendor directory of the installed tree, the value
of make print-INSTALL_LOCATION_EPICS followed by /vendor. The workflow
first writes configure/RELEASE.local with VENDOR_ULDAQ_PATH set to that
directory and OPEN62541_PATH set to a path relative to the opcua
configuration. It then clones uldaq-env and open62541-env from
https://github.com/jeonghanlee and installs both into the vendor directory.
| Workflows | Clone depth | Build targets in each vendor repository |
|---|---|---|
| Debian 12, Debian 13, Ubuntu 24.04, Ubuntu 26.04 | Full history | github |
| Rocky 8 | --depth 1 | init, conf.rocky8, build, and install |
| Rocky 10 | --depth 1 | init, conf.rocky10, build, and install |
Both vendor repositories provide conf.rocky10 as an alias of the existing
conf.rocky8 configuration recipe. Both names preserve the Red Hat linker
settings and installation prefix. Rocky 10 prints each vendor checkout’s
commit immediately after cloning it to identify the consumed source.
Build sequence in each OS workflow
The EPICS installation step runs these make targets in order.
github.check runs init, patch, vars, conf, check.module-deps,
build, and symlinks.
| Workflows | Make targets in order |
|---|---|
| Debian 12, Debian 13, Rocky 8 | github.check, install |
| Rocky 10 | init, patch, conf, check.module-deps, build, install, symlinks |
| Ubuntu 24.04, Ubuntu 26.04 | init, patch, conf, vars, check.module-deps, build, install, symlinks |
All six OS workflows run the module dependency gate check.module-deps
after patching and configuration and before compilation. A failing audit
stops the build.
Final checks in each OS workflow
The EPICS Environment Check step runs the same three targets in every OS
workflow, after the install:
make existprints the installed tree to depth 2.make check.envfails the job when the installedsetEpicsEnv.bashadds apvxs/bundlepath toLD_LIBRARY_PATH, or when it cannot be inspected.make check.depsfails the job on any runpath finding in the installed executables and shared libraries.
Tools and scripts reference lists the exit status of each check.
Workflow triggers and path filters
A paths-ignore filter skips a push only when every changed file matches one
of its patterns. A push that changes at least one other file runs the
workflow. A pull request runs every workflow that has a pull_request
trigger, with no path filter. The push triggers of the OS workflows and Linter
Run have no branch or tag filter, so a tag push also runs them; GitHub Actions
does not apply path filters to tag pushes.
| Workflow | Push trigger | Pull request trigger | Manual run |
|---|---|---|---|
| Each OS workflow | Any branch or tag; paths-ignore on branch pushes: **.md, docs/**, site-template/**, LICENSE, linter.yml, docs.yml, and the five other OS workflow files | Pull requests to master | No |
Linter Run (linter.yml) | Any branch or tag; paths-ignore on branch pushes: docs/** and ChangeLog.md | Pull requests to master | No |
Deploy Docs (docs.yml) | master only; paths: docs/src/**, docs/book.toml, and .github/workflows/docs.yml | None | Yes, through workflow_dispatch |
The workflow file names in the OS filters are paths under
.github/workflows/. An OS workflow does not ignore its own file, so a push
that changes only one OS workflow file runs that OS workflow and Linter Run.
Linter Run workflow steps
The Linter Run workflow has one job, Lint Code Base:
- It checks out the full history with
fetch-depth: 0and without stored credentials. - It runs
super-linter/super-linter@v8.7.0withVALIDATE_BASHset totrue.
The workflow grants no permissions at the top level. The job receives
contents: read, packages: read, and statuses: write.
Deploy Docs workflow steps
The Deploy Docs workflow builds this book with mdBook and publishes it to
GitHub Pages. It runs in the concurrency group pages and does not cancel a
run in progress. It holds contents: read, pages: write, and
id-token: write.
| Job | Container | Steps |
|---|---|---|
build | jeonghanlee/mdbook | Checks out the repository, prints mdbook --version, runs mdbook build docs, registers the checkout as a Git safe directory, fails when git status fails or reports a change under docs/src after the build, sets up Pages with actions/configure-pages, and uploads docs/book as the Pages artifact |
deploy | None | Runs after build and deploys the artifact to the github-pages environment |
Glossary of EPICS-env terms
This page defines each term the book uses with a specific meaning. The pages use each term only in the meaning given here.
Abbreviations used in this book
| Abbreviation | Meaning |
|---|---|
| ACF | Access security configuration file that an input/output controller (IOC) loads with asSetFilename |
| ASCII | American Standard Code for Information Interchange |
| CA | Channel Access, the EPICS network protocol that caget and caput use |
| CI | Continuous integration: the GitHub Actions workflows under .github/workflows/ |
| DBD | Database definition file |
| ELF | Executable and Linkable Format, the Linux binary format that readelf reads |
| EPICS | Experimental Physics and Industrial Control System |
| GCC | GNU Compiler Collection |
| IOC | Input/output controller: a process that serves EPICS records |
| IPv4 | Internet Protocol version 4 |
| NTP | Network Time Protocol |
| OS | Operating system |
| PR | Pull request |
| PV | Process variable: a named record field that clients read and write |
| PVA | pvAccess, the EPICS network protocol that pvget and pvxget use |
| RHEL | Red Hat Enterprise Linux |
| RPATH | Run-time search path entry in an ELF file; the link option --enable-new-dtags writes a RUNPATH entry instead |
| RUNPATH | Run-time library search path entry in an ELF file, written when the link uses --enable-new-dtags |
| SHA-256 | Secure Hash Algorithm 256-bit |
| TCP | Transmission Control Protocol |
Build and installation terms
| Term | Meaning |
|---|---|
$ORIGIN | The directory of the loaded ELF file; a RUNPATH entry that starts with it is relative to that file |
| configuration type | The <module>_CONF_TYPE value of a module: auto for a generated conf.<module> rule, custom for a hand-written one |
| declared dependencies | The <module>_DEPS list: the build.<module> targets that build before the module |
| installed tree | The directory $(INSTALL_LOCATION)/<release>/<os_id>-<os_version>/<base_version>, which make print-INSTALL_LOCATION_EPICS prints |
| module key | The upper-case name of a module’s SRC_* variables in configure/RELEASE, such as ASYN |
| module set | The EPICS modules that configure/RELEASE declares, each with a pin |
MODULESGEN.mk | The generated file configure/MODULESGEN.mk that holds each module’s repository URL, install directory, and source directory |
null.base | The empty target that stands for EPICS base as the root of every declared dependency list |
| pin | The tag or commit, SRC_TAG_<module_key>, that a module source checks out |
| release triple | The three variables SRC_NAME_<module_key>, SRC_TAG_<module_key>, and SRC_VER_<module_key> that declare one module |
| repository override | A SRC_GITURL_<module_key> line in configure/CONFIG_MODS for a module hosted outside epics-modules |
| unversioned link | The link modules/<module> that make symlinks creates to the versioned directory; modules/seq for the sequencer |
| vendor directory | The directory vendor/ in the installed tree that holds the uldaq and open62541 libraries |
| versioned directory | The install directory modules/<module>-<version> of one module; modules/seq-<version> for the sequencer |
Verification gate terms
| Term | Meaning |
|---|---|
| gate | The strict form of a check that stops a build or a CI run on a finding |
| lost runpath | A shared library in the installed tree that needs another library of the tree but has no $ORIGIN RUNPATH entry to find it |
| report form | The form of a check that prints its findings and exits 0 |
| strict form | The form of a check that exits with a failure code on a finding |
Patch carry terms
| Term | Meaning |
|---|---|
| carry patch | A patch under patch/ that carries an upstream fix onto the pinned EPICS base or pvxs source |
| carry set | Every carry patch whose name starts with the pinned version of its source |
| fixed module patch | A patch under patch/ with a fixed name for one module, applied by its own patch.<name>.apply target |
| version-anchored name | A carry patch name that starts with the source version, so a version bump drops the whole carry set |
Common iocsh fragment terms
| Term | Meaning |
|---|---|
| common iocsh fragment | One of the *.iocsh files that make install copies to modules/commonIocsh/iocsh in the installed tree |
| enable macro | A fragment macro that defaults to #--, which comments out an optional part; an empty value enables that part |
| evidence directory | The directory that verify_caputlog.py creates with --output; it must not exist beforehand |
IOCSH_TOP | The IOC macro that names the installed modules/commonIocsh directory; an IOC loads a fragment as $(IOCSH_TOP)/iocsh/<fragment>.iocsh |
| serial configuration file | An iocsh file that an IOC owns and that calls setSerialParams.iocsh once per serial port |
| soft IOC | A prebuilt IOC program that loads records from a database file, such as softIoc of EPICS base or softIocPVX of the pvxs module |