Article: Configuring your wrapper¶
To create a wrapper, you should use
cookiecutter (see
How to: Create an e3 wrapper with cookiecutter), or you could just create all the folders and the
files yourself. After having created the folder structure and the relevant
configuration files (in configure/
), you would generally set up the
${MODULE}.Makefile
.
The configure/
directory¶
If you used one of the template generators, the configuration of the EPICS base,
require version, and module version should have already been done for you. In
case you need to change them, the most important ones are typically
configure/RELEASE
and configure/CONFIG_MODULE
.
If your module depends on other modules (for example, it may depend on asyn,
StreamDevice, areaDetector, or any other number of modules), then you should
specify the dependencies in configure/CONFIG_MODULE
. This is done like so:
# DEPENDENT MODULE VERSION
ASYN_DEP_VERSION:=4.42.0
STREAM_DEP_VERSION:=2.8.18
ADCORE_DEP_VERSION:=3.10.0
The module Makefile¶
The module makefile (${MODULE}.Makefile
in the wrapper root directory) is
where we configure what gets built and how it gets built. For concreteness’
sake, let us focus on a specific module: iocStats. To be explicit, we are
currently looking at the makefile for version 3.1.16, built for require 4.0.0.
At the top of the makefile there is some boilerplate code which sets the build stage correctly:
where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ${E3_REQUIRE_TOOLS}/driver.makefile
include $(E3_REQUIRE_CONFIG)/DECOUPLE_FLAGS
Before we move on, we should take a brief detour to look at the output of this process (this will be covered more in-depth in Building and installing), i.e. a compiled and installed module. For iocStats 3.1.16 built under require 4.0.0, we find the following:
[iocuser@host:~]$ tree /epics/base-7.0.6.1/require/4.0.0/siteMods/iocstats/3.1.16+4/
/epics/base-7.0.6.1/require/4.0.0/siteMods/iocstats/3.1.16+4/
├── db
| ├── access.db
| ├── ...
| └── iocVxWorksOnly.template
├── dbd
| └── iocstats.dbd
├── include
| ├── devIocStats.h
| └── devIocStatsOSD.h
├── iocReleaseCreateDb.py
├── iocStats.iocsh
├── iocstats_meta.yaml
└── lib
└── linux-x86_64
├── iocstats.dep
└── libiocstats.so
The build process installs (potentially) several things to be available at run-time:
Database/template/substitution/protocol files
DBD (database definition) files
Header files for dependent modules
Snippets
Compiled libraries
Note
At ESS, startup scripts are modularised, and the convention is to separate out
functionality into separate startup snippets, that are named *.iocsh
.1
These are often colloquially referred to as iocsh files.
Database files¶
The database files are ones that you want available to an IOC at run-time. These
are defined by the variable ${TEMPLATES}
:
TEMPLATES += $(wildcard $(IOCADMINDB)/*.db)
TEMPLATES += $(wildcard $(IOCADMINDB)/*.template)
Note
As make allows for wildcards using the function above, this will include all
.db
and .template
files included in the directory specified by the variable
${IOCADMINDB}
.
Snippets¶
The snippets are portions of a startup script that you would like to call to
configure your module; for example, they may contain a call to
drvAsynIPPortConfigure
for a device that depends on asyn. These are simply
installed in the top-level module directory, and are controlled by the variable
${SCRIPTS}
:
SCRIPTS += $(IOCADMINSRC)/iocReleaseCreateDb.py
SCRIPTS += ../iocsh/iocStats.iocsh
Note that the second line refers to the parent directory of the module, i.e. the wrapper directory. It may often be the case that we want to install ESS-specific iocsh files, which are best kept in the e3 wrapper and not the module directory itself.
Header files¶
The header files are usually only necessary for a module that is a dependency of
other modules; for example, asyn declares many header files since many other
modules depend on that library. These are declared via the ${HEADERS}
variable.
HEADERS += $(DEVIOCSTATS)/os/default/devIocStatsOSD.h
HEADERS += $(DEVIOCSTATS)/devIocStats.h
Warning
By default, the headers are all flatly installed into the include/
directory;
that is, the two files listed are both installed directly as follows:
[iocuser@host:~]$ tree /epics/base-7.0.6.1/require/4.0.0/siteMods/iocstats/3.1.16+4/include/
/epics/base-7.0.6.1/require/4.0.0/siteMods/iocstats/3.1.16+4/include/
├── devIocStats.h
└── devIocStatsOSD.h
If you have two files in separate directories but with the same name, then you cannot install them this way.
From require 3.3.0 onwards, you can define the KEEP_HEADER_SUBDIRS
variable
in the module Makefile; KEEP_HEADER_SUBDIRS
should contain the list of
top-level header directories where you want to train the directory structure in
the installation process.
KEEP_HEADER_SUBDIRS += $(GMTOP)
where you have defined GMTOP
elsewhere in the module Makefile.
Compiled libraries¶
The library that is built is a shared library that results from compiling and
linking all of the source files into a single shared object. These are managed
by the variable ${SOURCES}
.
SOURCES += $(DEVIOCSTATS)/devIocStatsAnalog.c
SOURCES += $(DEVIOCSTATS)/devIocStatsString.c
SOURCES += $(DEVIOCSTATS)/devIocStatsWaveform.c
SOURCES += $(DEVIOCSTATS)/devIocStatsSub.c
SOURCES += $(DEVIOCSTATS)/devIocStatsTest.c
SOURCES += $(DEVIOCSTATS)/os/Linux/osdCpuUsage.c
SOURCES += $(DEVIOCSTATS)/os/Linux/osdCpuUtilization.c
SOURCES += $(DEVIOCSTATS)/os/Linux/osdFdUsage.c
SOURCES += $(DEVIOCSTATS)/os/Linux/osdMemUsage.c
SOURCES += $(DEVIOCSTATS)/os/default/osdWorkspaceUsage.c
SOURCES += $(DEVIOCSTATS)/os/default/osdClustInfo.c
SOURCES += $(DEVIOCSTATS)/os/default/osdSuspTasks.c
SOURCES += $(DEVIOCSTATS)/os/default/osdIFErrors.c
SOURCES += $(DEVIOCSTATS)/os/default/osdBootInfo.c
SOURCES += $(DEVIOCSTATS)/os/posix/osdSystemInfo.c
SOURCES += $(DEVIOCSTATS)/os/posix/osdHostInfo.c
SOURCES += $(DEVIOCSTATS)/os/posix/osdPIDInfo.c
Note that you can also include sequencer files or C++ files here as well. The
build process will understand based on the file extension how to compile it
accordingly. If you use any sequencer files, then an appropriate .dbd
file
will be created with the correct database definitions to register your sequencer
program.
Database definition files¶
Any .dbd
files that you would like to add are combined into a single module
.dbd
file that is loaded when the module is loaded at IOC startup. These are
governed by the variable ${DBDS}
:
DBDS += $(DEVIOCSTATS)/devIocStats.dbd
Dependencies¶
The build process is smart enough to detect any code-based dependencies. For
example, if you include the header files from iocStats above in one of your
source code files, then driver.makefile
will infer that your module depends on
iocStats; when your module is loaded, it will also load the correct version of
iocStats first.2 This raises two questions:
How does it detect the correct version?
What about non code-based dependencies?
The correct version is detected with the following code:
calc_VERSION=$(CALC_DEP_VERSION)
Note that ${CALC_DEP_VERSION}
should be specified in
configure/CONFIG_MODULE
. In principle it does not need to be there, but it is
clearer and easier to maintain if the dependencies are consistently placed in
the same file.
Especially note that the variable name ${calc_VERSION}
must match exactly
(including case) the name of the installed module.
Warning
Joint with the release of require 3.3.0 there was a switch to all modules being lower-case. So if you have a dependency on ADCore, then from require 3.3.0 onwards you should use the definition:
adcore_VERSION=$(ADCORE_DEP_VERSION)
while for earlier versions of require you would instead use:
ADCore_VERSION=$(ADCORE_DEP_VERSION)
For non-code based dependencies (such as StreamDevice and protocol files, or
the records introduced in the calc module), you have to explicitly state the
requirement using the ${REQUIRED}
variable:
REQUIRED += calc
Note
As in the example above, the module name must exactly match the name of the installed module.