With driver.makefile, it is possible to build IOC software modules such
as drivers, state notation code, records, etc. with a minimum of
configuration. Many things are detected automatically but can be changed if
necessary. The installed module can be loaded on the IOC with the require
command.
The modules are built for each combination of EPICS base version (e.g. 3.13.10, 3.14.12, 7.0.4.1,...) and IOC architecture (e.g. T2-ppc604, SL6-x86_64, RHEL7-x86_64,...). This allows to run the same software on IOCs with different EPICS versions or operating systems.
PSI: Some of the settings and methods described here are specific to the setup at PSI. If you use this software somewhere else, details may differ. PSI specific information is emphasized like this.
PSI: Best log in on one of our software development computers like sls-lc, hipa-lc,... to make sure all needed EPICS software is installed and write access to the installations locations is granted.
A module built with driver.makefile can be installed into a module pool
common to all IOCs (of one facility) where require
can find it. The module
pool can contain several versions of the same module and require
can be
used to load a specific version or simply the highest one.
The default location of the pool is /ioc/modules/
but can be changed with
the environment variable EPICS_MODULES
. It contains one subdirectory for
each module which contains subdirectories for each version of the module.
All files of a module that are needed at run-time are installed into that subdirectory. Thus everything needed to use the module is found at the same place. This includes dynamically loadable libraries for all architectures and EPICS versions, a DBD file for each EPICS version, module dependency information, optionally C/C++ header files, DB templates, startup script snippets, and arbitrary other files.
PSI: A module does not need to be installed into the module pool. It is
also possible to install a (private) module with ioc install
into the IOC
start directory. In that case, only this IOC can use it. Nevertheless
driver.makefile can be used to compile such a module and require
can be
used to load it. Only startup scripts and templates are not supported by
require
in this case (and not really needed).
In order to use a module, its library needs to be loaded, its .dbd file needs to be loaded and (if using EPICS 3.14 or higher) its initialization function needs to be called.
Some modules may depend on other modules (for example many modules depend on asyn). In that case it is necessay that the other module is loaded first and that conflicts are resolved in case multiple different versions of the same module are requested.
To make loading modules easier, the require
command can be used in IOC
startup script.
require "<module>" [,"<version>"] [,"<macro1>=<value2>, <macro2>=<value2>"]
This performs all necessary loading and initialization, avoids loading a
library twice and keeps an eye on version conflicts. If a required module
depends on an other module, require
recursively invokes itself to load
the other module fist.
The two arguments, the version and the macro list are both optional. The order does not matter, thus macros can be provided without a requesting a specific version.
Version strings can consist of one, two or three numbers, separated by a dot.
If a version string with less than than three numbers is used, require
loads the highest available version that matches the given first numbers.
A +
after a number means that the version may be higher but not lower.
If the version string is omitted, require
loads the highest available
version of the module (which may be different for different architectures
or EPICS base versions).
Version strings that do not consist of numbers are considered test versions and are only loaded when requested explicitly. I strongly suggest not to use test versions in production.
If the version string is "ifexsists"
, then the highest existing version
is loaded as if no version was given. But failure to find the module (for
the current architecture and EPICS base version) is not an error. This can
be useful for generic modules that exist only on certain architectures.
If the version string is "none"
, then nothing is done. The module is
skipped. This can be useful if the version string is a macro which may be
set to different versions (including "none"
) depending on external
conditions.
Examples:
# load highest version
require "module"
# load test version
require "module", "username"
# load highest version but ignore if the module does not exist
require "module", "ifexists"
# load the highest 1.2.x version
require "module", "1.2"
# load the highest 1.x version and make sure that x>=2 (used by dependency files)
require "module", "1.2+"
# load exactly version 1.2.3 and pass macros to a startup script
require "module", "1.2.3", "macro1=value1, macro2=value2"
# load highest version and pass macros to a startup script
require "module", "macro1=value1, macro2=value2"
# skip the module
require "module", "none", "macro1=value1, macro2=value2"
If an already loaded module is required a second time, the requested version is checked for compatibility with the already loaded one. If the versions are incompatible the startup script is aborted with an error message.
An already loaded version is considered compatible if:
- no specific version was required
- the already loaded version matches the required version exactly
- major and minor numbers are the same and already loaded patch number is equal to the required one or higher
- major numbers are the same and already loaded minor number is higher than the required one
- the already loaded version is a test version and the required version is not a test version
Versions with different major numbers are never compatible and different test versions are never compatible. Modules can be built in a way that enforece stricter compatibility rules, that is either the major and minor version numbers must match exactly or even the patch level must match exactly as well. See below for details.
PSI: In our environment with the possibility to install multiple projects on one IOC, the same module may be required by more than one project with the potential of conflicting version requests.
The require
command uses the environment variable EPICS_DRIVER_PATH
to
search for modules. Usually, the variable is set up by the iocsh
wrapper
script such that require
searches first locally in the current directory
(usually the IOC start directory), then in the module pool /ioc/modules/
(or $EPICS_MODULES
if set).
For every loaded module, require
creates one stringin record with the
name $(IOC):$(MODULE)_VERS
which contains the version of the module.
(Only if require
is called before iocInit
because it is not allowed to
create records after iocInit
.)
Furthermore, the following global records are created:
- The STRING waveform record
$(IOC):MODULES
contains a list of all loaded modules. - The STRING waveform record
$(IOC):VERSIONS
contains the module versions (indices match the former record). - The CHAR waveform record
$(IOC):MOD_VER
contains the list of module and version pairs as a long string with newline separated lines. The list is formatted so that the string prints nicely as a table with fixed width fonts.
The IOC
environment variable is set by the iocsh
wrapper script.
Several environment variables are set up by require
that can be used in
the IOC startup script or in the startup script snippets of the modules.
$(EPICS_RELEASE)
is the EPICS base release in use, e.g. "3.14.12".$(EPICS_BASETYPE)
contains the first two components of the above, e.g. "3.13" or "3.14"$(EPICS_HOST_ARCH)
is the target architecture, e.g. "T2-ppc604".$(T_A)
the same, just a shorter name.$(OS_CLASS)
e.g. "Linux", "vxWorks", "WIN32"$(IOC_DIR)
contains the absolute path of the directory in which the IOC started.$(MODULE)
is set to the name of the current or most recently required module (even if it had been loaded already earlier).$($(MODULE)_VERSION)
is the version string of the module.$($(MODULE)_DIR)
is the absolute path of the module directory. Here are the startup scripts located.$(MODULE_DIR)
the same, but overwritten each timerequire
is called.$(SCRIPT_PATH)
contains the directories of all loaded modules with scripts in reverse order (after "."). This allows to userunScript
without explicit path (see below).$($(MODULE)_TEMPLATES)
is the absolute path for the db templates of a module (if the module has templates).$($(MODULE)_DB)
the same, just a shorter name.$(TEMPLATES)
the same, but overwritten each timerequire
is called. This is reset to the original value at IOC start if the module has no templates.$(EPICS_DB_INCLUDE_PATH)
contains all the template directories of all loaded modules in reverse order (after ".").
In particular the EPICS_DB_INCLUDE_PATH
variable allows to load template
files from a module with dbLoadTemplate
, dbLoadDatabase
or
dbLoadRecords
without the need to have them locally in the IOC start
directory. Only the substitution file needs to be stored locally. The
directory .
is always first in that path so that local templates can
overwrite module templates.
If the module has a default startup script snippet, it is executed before
require
returns and macros in the startup script snippet are replaced
with the substitutions passed to require
or with environment variables
if no matching substitution was given (including the variables set by
require
, see above).
Some modules use this method to load default templates or to initialize
hardware supported by the module in a standard way for or any other module
initialization.
Script snippets are searched by require
in the following order:
$(EPICS_HOST_ARCH)-$(EPICS_RELEASE).cmd
(e.g.RHEL7-x86_64-3.14.12.cmd
)$(EPICS_HOST_ARCH)-$(EPICS_BASETYPE).cmd
(e.g.RHEL7-x86_64-3.14.cmd
)$(OS_CLASS)-$(EPICS_RELEASE).cmd
(e.g.Linux-3.14.12.cmd
)$(OS_CLASS)-$(EPICS_BASETYPE).cmd
(e.g.Linux-3.14.cmd
)startup-$(EPICS_RELEASE).cmd
(e.g.startup-3.14.12.cmd
)startup-$(EPICS_BASETYPE).cmd
(e.g.startup-3.14.cmd
)$(EPICS_HOST_ARCH).cmd
(e.g.RHEL7-x86_64.cmd
)$(OS_CLASS).cmd
(e.g.Linux.cmd
)startup.cmd
Only the first one found is executed automatically.
β Startup script snippets are only executed if require
is
called before iocInit
because it is illegal to do certain actions after
iocInit
.
In particular no record must be created and not driver must be initialized
after iocInit
.
If the module was already loaded, the startup script snippets is called again if and only if substitutions are passed.
Other than the default startup scripts can be called with runScript
which
uses the same macro substitution:
runScript "otherscript.cmd", "MACRO=VALUE, ..."
runScript "$(module_DIR)/somescript.cmd", "MACRO=VALUE, ..."
Keep in mind that the runScript
searches $(SCRIPT_PATH)
for scripts
without a path, which contains all loaded modules in reverse order
(after "."). That means that local scripts are always found first, followed
by the most recently loaded modules. Thus using a path is often notnecessay.
But to be safe, the variable $(module_DIR)
can be used explicitly (with
the name of the module).
Scripts executed by require
or runScript
can use local variables and
integer arithmetic.
Local variables hold strings like macros or environment variables, but
arithmetic expressions are evaluated when the variable is assigned.
These variables can be used with the standard syntax $(variable)
or
${variable}
in the same script where they are defined.
To set a local variable, use the =
character.
variable=value
The variable must consist of alphanumeric characters or underscores.
No spaces are allowed before the =
and spaces after the =
are part of the value.
The value consists of strings and integer expressions.
π‘ To assign instead to a global variable in vxWorks use a space
before the =
.
- Everything in quotes is a string and copied without evaluation (but the quotes are removed).
- Every sequence of unquoted words and everything inside an unquoted pair of parentheses that can be evaluated as an integer expression is substituted by the result.
- Everything else is a string and copied without modification. That includes strings inside words that may look like expressions.
Integer expressions consist of integer literals in either decimal, octal or
hexadecimal notation (with the usual prefixes 0
for octal and 0x
for
hexadecimal), parentheses ()
and the operators +
, -
, *
, /
, %
(modulo), **
(exponential), <<
, >>
, >>>
(unsigned shift),
<
, <=
, >
, >=
, ==
, !=
(not equal), <=>
(comparision, results in
-1, 0 or 1), &
, |
, ^
(bitwise and, or, xor), &&
, ||
(logical and,
or), ?
(not equal to 0), ?
:
(if then else), ?:
(if else).
β All $(...)
or ${...}
have already been substituted with
their (string) values before arithmetic evaluation starts. That can be
confusing with operator priorities. For example x=5*$(MACRO)
with
MACRO="1+2"
results in 7, not 15. Use x=5*($(MACRO))
or MACRO="(1+2)"
to get 15 in this case.
Division by 0 or modulo 0 gives undefined results (in fact 0, but do not rely on it).
A value (literal integer or ()
expression) followed by ?
followed by a
first expression, :
and a second expression computes to the first
expression if the value is not 0, else to the second expression.
A value followed by ?:
followed by an expression computes to the
value if that one is not 0, else to the expression.
A value followed by ?
computes to 0 if the value is 0 and to 1 otherwise.
An expression can be prefixed with a printf() style integer format to change
formatting of the result. Valid formats are: %
, optionally followed by any
of +-#0
or space, optionally followed by a positive integer number,
followed by one of diouxXc
. See
man printf
for details. The default format is %d
.
β Do not quote the format string or it will simply be a string copied literally.
π‘ To see the result of an arithmetic expression you can add a comment
line containing the variable reference: #$(variable)
Examples: (with the result made visible as #$(x)
).
x=Buy 4 + 2*3 eggs
#Buy 10 eggs
x="Buy 4 + 2*3 eggs"
#Buy 4 + 2*3 eggs
x=Buy 4 + 2*3eggs
#Buy 4 + 2*3eggs
x=Buy(4 + 2*3)eggs
#Buy10eggs
x="Buy(4 + 2*3)eggs"
#Buy(4 + 2*3)eggs
x=010
#8
x=%0o4+4,%0x4*4,%04d4**4
#10,10,0256
x=7?,7?2:3,7?:5
#1,2,7
x=0?,0?2:3,0?:5
#0,3,5
β This type of arithmetic only works in local variable
assignments and thus only in scripts executed by runScript
.
To use driver.makefile create a GNUmakefile
, typically in the top level
module source directory which includes /ioc/tools/driver.makefile
.
Often, that is the only thing to do. No knowledge about how make
works is
needed. The idea is to make the GNUmakefile
for a simple module as simple
as possible.
Example GNUmakefile:
include /ioc/tools/driver.makefile
This detects most things automatically, as long as all files are in the top
level directory of the module and use standard file extensions. But it is
possible to change the default behavior by defining variables in the
GNUmakefile
. If doing so, keep the include line at the top and add
variable definitions below. Variables contain one word or lists of words
(e.g. file names). More words can be appended to variables. Appending to an
empty or not existing variable is like setting it.
Examples:
include /ioc/tools/driver.makefile
# build for both, vxWorks and Linux
BUILDCLASSES += Linux
# only use the following source files
SOURCES = file1.c file2.c
SOURCES += file3.c
Then the module can be built with make
or make build
in the directory
with the GNUmakefile
. The built module can be installed with
make install
. This automatically calls build
if not yet done or if files
have changed since the last built. An installation can be deleted with
make uninstall
. The created files from the build process can be cleaned up
with make clean
. To find out which module version will be built (and why)
use make version
. If you are unsure about the options or variables try
make help
. Several commands can be chained like
make uninstall clean build install
.
GNU make
, which EPICS uses, reads commands from GNUmakefile
, makefile
,
or Makefile
, in that precedence (unless told otherwise with -f
). All
three names work with driver.makefile but GNUmakefile
is preferred, because
the name Makefile
is already used by many third party modules for the
standard EPICS build method. Thus when having a module that should be
compatible with the standard EPICS build method and with driver.makefile,
it makes sense to keep Makefile
for the standard method.
GNUmakefile
takes precedence over Makefile
if both exist
(see also: man make
).
Thus to compile with driver.makefile, simply write make
but to compile the
standard EPICS way write make -f Makefile
. The name makefile
, (which
would also take precedence over Makefile
) should not be used as an
alternative to Makefile
because Windows ignores the different
capitalization.
When giving the module sources to other institutes which do not use
driver.makefile, simply remove the GNUmakefile
but keep (or create) the
Makefile
for the standard EPICS build method..
The version is generated from a GIT tag which must consist of two or three
numbers separated by .
or (for backward compatibility) must end in two or
three numbers separated by _
. A missing third number is replaced with 0.
All used files must be committed, the commit must be tagged and pushed together with the tag and the tag on the remote server must match the local tag.
Examples: 1.2.3
, mydriver_7_2
The first number is the major version number. It must be incremented if any change in the module is not backward compatible. Such changes include:
- Removing or renaming features, for example interface functions or IOC shell functions, templates, macros, interface headers. (Renaming or changing only internally used functions or header files do not change the interface and thus do not require a new major version number. Thus do not install internal header files unnecessarily.)
- Changing the behavior of such features, for example the meaning or order of function arguments.
- Adding macros without default value to a template
- Changing structure layouts in interface header files
- Basically any modification that makes it impossible to use the newer version instead of the older one.
The second number is the minor version number. It resets to 0 whenever the major number is incremented. Increment the minor version number whenever a new feature is added but where backward compatibility is kept. Such changes include:
- Adding new features like functions, templates, header files.
- Adding new macros with default values to a template so that the instantiated records do not change
- Adding new debugging features or changing debug messages
The third number is the patch level. It resets whenever the minor version number is changed. Increment the patch level with every bug fix. Do not add new features without using a new minor version number.
If the current source is not tagged, not committed, not pushed or not even
tracked by GIT, or the remote tag is not on the same commit as the local one,
then a test version is built.
By default the version string is the content of the $USER
variable. You
can overwrite this by calling make LIBVERSION=versionstring
. Be careful
when doing this!
β Never "recycle" version numbers by overwriting already installed (non-test) versions when anything has changed! Use new version numbers!
When starting, driver.makefile reads a configuration file config
from the
directory where driver.makefile is installed, by default /ioc/config
.
In this file, you can overwrite default configuration variables, for example
DEFAULT_EPICS_VERSIONS
, BUILDCLASSES
, EPICS_MODULES
, EPICS_LOCATION
,
EXCLUDE_ARCHS
, or whatever variable you may want to define.
This overwrites the default settings in driver.makefile but is overwritten
by settings in the GNUmakefile of the module, which in turn is overwritten
by any variables set on the command line when make
is called.
This variable overrides the default module name, which is the name of the
directory in which make
has been invoked, i.e. where the GNUmakefile
is
located, typically the top level directory of the module source. Only if the
directory has the name src
or snl
, the name of the parent directory is
taken. It is required to change the module name if the directory contains
characters that cannot be part of a C variable name, e.g. a space, a dot, or
a minus.
π‘ Older versions of driver.makefile used the PROJECT
variable
instead. It is still supported for backward compatibility.
In order to stay compatible with older EPICS 3.13 drivers that are not
operating system independent, the default is to compile for vxWorks only.
This is controlled by the BUILDCLASSES
variable. Add Linux
to it to
compile for both, vxWorks and Linux. Replace it with Linux
to compile for
Linux only. For Windows builds, use WIN32
(for 64 bit builds too).
PSI: The BUILDCLASSES
variable is overwritten in the
global configuration to contain all three,
Linux
, vxWorks
and WIN32
. Overwrite it if your module does not compile
for all OS classes.
β Not all cross builds are necessarily available for all faclilities. It depends on what is included in the EPICS installations.
Example:
BUILDCLASSES = Linux
β Older code that uses vxWorks specific functions or headers will fail to compile on any other operating system.
The variable DEFAULT_EPICS_VERSIONS
contains a list or EPICS base versions
to use for building modules. This variable is a candidate to be modified in
the global configuration as it may change over the
years.
Example:
DEFAULT_EPICS_VERSIONS = 3.14.12 3.15.5 7.0.4.1
β Do not set this variable in the GNUmakefile
of a module as
this would make the module incapable of being reduilt with future EPICS base
installations.
It is not necessary, that all listed EPICS base versions are actually available on the compile host (some may only be available on certain host OS versions or architectures due to compiler requirements).
The variable EPICS_VERSIONS
contains all elements of
DEFAULT_EPICS_VERSIONS
which are actually available (for which the
configured compiler is found).
I strongly suggest not to overwrite this variable in the module either.
Instead, use the EXCLUDE_VERSIONS
variable described below.
The default is to compile for all available EPICS versions. But sometimes code cannot be compiled with all versions. Therefore specific versions like 3.13.10 or a group of versions like 3.14 or 7 can be excluded from compilation.
Example:
# do not build this module for any EPICS base 3.13 or 7 versions
EXCLUDE_VERSIONS = 3.13 7
If code cannot compile for some IOC architectures, you can skip them. All architectures starting or ending with one of the words listed here are excluded from building.
Example:
# do not build for mvl40-xscale_be or any eldk* or any *ppc604 architecture
EXCLUDE_ARCHS = mvl40-xscale_be eldk ppc604
Similar to EXCLUDE_ARCHS
but more flexible and defining a positive list
instead of a negative list. Build only for architectures that match one of
the patterns in the variable. Use %
as a wildcard (only once per pattern).
Example:
# build only for Scientific Linux and for PPC 604 architectures
ARCH_FILTER = SL% %ppc604
β Which architectures are available depends on the EPICS base installations. Some facilities may add architectures to the standard set.
PSI: Instead of the standard linux-x86
and linux-x86_64
, we use more
specific names which allows us to support code for different Linux versions
on the same NFS server. Currently supported Linux host architectures are
SL6-x86_64
and RHEL-x86_64
.
A module can consist of C/C++ code implementing EPICS drivers, device support, additional record types, sub/genSub/aSub record functions, SNL (State Notation Language) code, or any other code that should run on an IOC. All the module code is compiled and linked into one dynamically loadable library for each target architecture and EPICS version.
By default, driver.makefile finds source code files automatically: All C/C++
(*.c
, *.cc
, *.cpp
) and SNL (*.st
, *.stt
) code in the top level
directory of the module. Hidden files (staring with .
) as well as files
starting with ~
(backup files of some editors) are ignored.
To change this, list the source code files in the variable SOURCES
. If
that variable is defined, no automatic detection of source code is done.
For code only to be compiled with certain EPICS versions, OS classes, or
architectures, use variables like SOURCES_*
, for example SOURCES_3.13
,
SOURCES_3.14.12
, SOURCES_7
, SOURCES_Linux
, SOURCES_vxWorks
,
SOURCES_7.0.6_SL6
.
For backward compatibility, SOURCES_3.14
, SOURCES_3.14_Linux
and such
are built for any EPICS release from 3.14 on like 3.15 and 7.
Example:
include /ioc/tools/driver.makefile
SOURCES += mycode.c
SOURCES += subdir/othercode.cc
SOURCES += statemachine.st
SOURCES_3.13 += codeOnlyFor3.13.c
SOURCES_3.14 += codeFor3.14orHigherIncluding7.c
SOURCES_3.14.12 += codeOnlyFor3.14.12.c
SOURCES_3 += codeOnlyFor3.c
SOURCES_7 += codeOnlyFor7.c
If all files are OS class specific or EPICS base version specific so that
SOURCES
would be empty, automatic source code detection can be suppressed
by setting SOURCES=-none-
.
A module often also contains one or more DBD files (for record types, device support, IOC functions,...).
Some DBD files are also generated automatically if necessary:
For EPICS 3.14, a DBD file is generated for each SNL source file (*.st
,
*.stt
) containing the names of the state programs.
By default all DBD files in the top level directory are combined into one module DBD file for each EPICS version (the files may differ in details between different EPICS versions).
To change this, list the files in the variable DBDS
. EPICS base version
dependent files can be listed in variables like DBD_3.13
or DBD_3.14.12
.
β There is no support for architecture dependent DBD files.
If a module provides features (in particular functions) to be used by other
modules, it contains one or more C/C++ header files which can be included
by the code of other modules. All such header files must be specified in the
variable HEADERS
.
β Only install the interface headers, i.e. those header files that are required by other (dependent) code. Do not simply install all header files found in the module!
Some header files are generated automatically, in particular record type and
menu headers, which are created from DBD files. These header files are
automatically installed and need not be added to the HEADERS
variable.
The header search path of the compiler (-I
) is set up automatically to
find the header files of the highest version of each other installed module.
If necessary, it is possible to use a different version of a module by
defining a variable <module>_VERSION
.
Example:
asyn_VERSION = 4.8.1
π‘ As all other modules are searched for header files (in unspecified
order), it makes sense to avoid too generic header file names, such as
version.h
. At least to not install them.
π‘ OS class dependent header files which are located in an appropriate
subdirectory like os/Linux/
keep their location in such a subdirectory
and will not found when compiling other OS classes.
Including an installed header file of an other module creates a dependency
that is detected automatically by driver.makefile and resolved by require
.
Dependencies that cannot be detected automatically (because no header file
is involved, e.g. when a db template file is required) can be added manually
using one the variables REQUIRED
, REQUIRED_<EPICS_version>
,
REQUIRED_<OS_CLASS>
, REQUIRED_<ARCHITECTURE>
or similar.
Example:
REQUIRED_3.13 = timestamp
When a module is loaded with require
, all its dependencies are resolved by
first loading highest compatible minor version of the depended on module.
β Some third party software modules follow other version numbering
conventions and may no adhere to the rule not to break backward compatibility
without incrementing the major version number. In such cases, define either
USE_EXACT_MINOR_VERSION
or even USE_EXACT_VERSION
in the
GNUmakefile
of that module. This will prevent reqire
to assume that
higher minor versions (or even patch levels) are backward compatible when
a dependency on that module is found.
Problematic header files installed by another module may be ignored, e.g.
in case of file name clashes. Set the variable IGNORE_MODULES
to a list
of modules whose header files should not be found automatically.
Example:
IGNORE_MODULES = motorBase asynMotor
β Do not install different versions a module with headers under different names (for the same EPICS version). This will inevitably create file name clashes which cause problems to all other modules that use this module.
Modules can also provide db EPICS template files. This is useful if a driver is typically used with one or more matching template files. The template files are installed together with the other module files for each version. Thus they may differ between versions (e.g. in order to match the changing features of the driver). It is even possible to have module that contain only template files and no code. Also substitution files may be provided here.
By default, all templates (*.template
, *.db
) and substitution files
(*.subs
) in the top level directory are installed.
To change this, list the files in the variable TEMPLATES
or
TEMPLATES_<EPICS_version>
.
A module can contain startup script snippets to be executed automatically after a module is loaded or as requested by the user. (See above.)
The script snippets may contain macros using the $(MACRO)
or
$(MACRO=default)
syntax which are replaced by actual values passed to the
require
command as "MACRO=VALUE, ..."
, by environment variables (in
particular $(IOC)
is the IOC name when using the iocsh
wrapper script),
or using a default value (in that precedence).
See above for environment variables that are set by
require
automatically.
By default, all *.cmd
files in the top level directory are installed.
To change this, list the files in the variable SCRIPTS
or
SCRIPTS_<EPICS_version>
.
β Please be aware that environment variables set in the startup
script snippet with epicsEnvSet
keep their values even after the script has
finished and may affect other modules loaded later.
To set local variables, use the syntax variable=value
instead. These
do not keep their values when the script snippet has finished.
For more details see above.
A module may have related user interface (UI) screens. They can be installed
with make installui
and uninstalled with make uninstallui
.
Currently caQtDM UIs are supported. By default all qt/*
files are installed,
but that can be overwritten with the QT
variable.
The installation location is not the module install directory, but the
global location ${CONFIGBASE}/qt/
if CONFIGBASE
is defined. If not
the location is ${EPICS_MODULES}/qt/
which in turn defaults to
/ioc/modules/qt/
.
π‘ Other UI types may be supported by defining a INSTALL_UI_RULE
,
passing the name of the variable, the install location and the default search
pattern. This is how QT
files are configured:
$(eval $(call INSTALL_UI_RULE,QT,${CONFIGBASE}/qt,qt/*))
It is not possible to have different versions installed like for the modules themselves, because there is no way for a client to know which module versions are currently loaded on the IOCs (which may even vary among IOCs). It is assumed that the latest UI version is more or less compatible with older module versions.
For this reason, you have to type make installui
explicitly and not
only make install
. This will first uninstall all UI files beonging to older
versions of the module, just like make uninstallui
.
(For this purpose, driver.makefile tracks the installed UIs in a hidden file
in the install location).
The way EPICS software is built has changed a lot from R3.13 to R3.14 due to the requirement to run on other oprating systems than just vxWorks. This makes it hard to write a module that compiles with both EPICS release families. However, driver.makefile contains some features to hide this from the developper.
For most 3.13 code, it is possible to compile it straight forward or with minor modifications for 3.14. The additional code that is required by EPICS 3.14 is generated automatically by driver.makefile. Of course, code using vxWorks functions and headers cannot be compiled for other operating systems, even when using EPICS 3.14.
For a wide range of 3.14 code, it is possible to compile it for 3.13 without modifications. New 3.14 specific functions come from a special compatibility library which is loaded automatically when loading this module. EPICS 3.14 specific DBD file entries are automatically deleted from module DBD files for 3.13.
Some EPICS 3.14 header files do not include all other headers they did in
3.13. So it may happen that you have to put additional #include
directives
into 3.13 code to compile it for 3.14. A typical candidate is recGbl.h which
is no longer included by regSup.h. This modification is fully backward
compatible.
EPICS 3.13 was not C++ aware. Thus in 3.13, EPICS headers have to be
wrapped in extern "C" { }
. But EPICS 3.14 does use C++, so in 3.14, EPICS
headers must not be wrapped. This can be solved by including
epicsVersion.h and some #ifdef BASE_VERSION
statements.
This works because the macro BASE_VERSION
is not defined in 3.14. any more.
Only wrap EPICS headers. Do not wrap operating system headers like
stdio.h.
Example:
// #include system headers here
#include <epicsVersion.h>
#ifdef BASE_VERSION
// This is for EPICS 3.13
extern "C" {
#endif
// #include EPICS headers here
#ifdef BASE_VERSION
}
#endif
In EPICS 3.14, it is possible to define device support and driver structures
static
. This is not compatible with EPICS 3.13. To compile the code for
EPICS 3.13., remove the static
keyword (sometimes hidden in a macro called
LOCAL
).
Usually third party packages come with a structure and Makefiles that follow
the standard makeBaseApp project layout. They typically have a Makefile
that works with either EPICS 3.13 or 3.14 but not both. And often source
files are located in sub-directories. The easiest way to deal with these
projects is to create a GNUmakefile
in the top level directory and list
all require source files in the SOURCES
variable.
PSI: If you do not know which modules are needed for a given set of records,
use externalLinks
:
externalLinks --require *.subs
In addition to printing the list of link targets not resolved internally
within the records defined by the given substitution files and thus
finding potentioal typos (the original purpose of the tool), with the
--require
option it also prints a list of modules required. It does this
by searching all installed modules for missing record types device supports.