Files

893 lines
37 KiB
Markdown

# driver.makefile and `require`
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](https://www.psi.ch). 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._
## Module Pool
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)._
## Using `require`
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
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"
```
#### Version compatibility
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](#header-files-and-dependencies-headers-_version-and-required-variables)
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).
### Version Records
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.
### Environment Variables
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 time `require` is called.
* `$(SCRIPT_PATH)` contains the directories of all loaded modules with
scripts in reverse order (after "."). This allows to use `runScript`
without explicit path (see [below](#startup-script-snippets)).
* `$($(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 time `require` 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.
### Startup Script Snippets
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](#environment-variables)).
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:
1. `$(EPICS_HOST_ARCH)-$(EPICS_RELEASE).cmd`
(e.g. `RHEL7-x86_64-3.14.12.cmd`)
2. `$(EPICS_HOST_ARCH)-$(EPICS_BASETYPE).cmd`
(e.g. `RHEL7-x86_64-3.14.cmd`)
3. `$(OS_CLASS)-$(EPICS_RELEASE).cmd` (e.g. `Linux-3.14.12.cmd`)
4. `$(OS_CLASS)-$(EPICS_BASETYPE).cmd` (e.g. `Linux-3.14.cmd`)
5. `startup-$(EPICS_RELEASE).cmd` (e.g. `startup-3.14.12.cmd`)
6. `startup-$(EPICS_BASETYPE).cmd` (e.g. `startup-3.14.cmd`)
7. `$(EPICS_HOST_ARCH).cmd` (e.g. `RHEL7-x86_64.cmd`)
8. `$(OS_CLASS).cmd` (e.g. `Linux.cmd`)
9. `startup.cmd`
Only the first one found is executed automatically.
:exclamation: 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).
#### Local Script Variables
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.
:bulb: 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).
:exclamation: 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`](https://man7.org/linux/man-pages/man3/printf.3.html)
for details. The default format is `%d`.
:exclamation: Do not quote the format string or it will simply be a string
copied literally.
:bulb: 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
```
:exclamation: This type of arithmetic only works in local variable
assignments and thus only in scripts executed by `runScript`.
#### Command error handling
If a command executd by `runScript` reports failure (not al actually do this
when failing), the script is aborted. This can be avoided by starting the
line with a `?` prefix. The `?` itself is not executed.
**Example**:
```
?system("false")
```
## Using driver.makefile
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`.
### GNUmakefile or Makefile?
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`](https://man7.org/linux/man-pages/man1/make.1.html)).
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..
### Versions and Tags
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!
:exclamation: Never "recycle" version numbers by overwriting already
installed (non-test) versions when anything has changed! Use new version
numbers!
### Global configuration
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.
### `MODULE` Variable
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.
:bulb: Older versions of driver.makefile used the `PROJECT` variable
instead. It is still supported for backward compatibility.
### `BUILDCLASSES` Variable
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](#global-configuration) to contain all three,
`Linux`, `vxWorks` and `WIN32`. Overwrite it if your module does not compile
for all OS classes._
:exclamation: Not all cross builds are necessarily available for all
faclilities. It depends on what is included in the EPICS installations.
**Example:**
```
BUILDCLASSES = Linux
```
:exclamation: Older code that uses vxWorks specific functions or headers will
fail to compile on any other operating system.
### `EPICS_VERSIONS` and `DEFAULT_EPICS_VERSIONS` Variables
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](#global-configuration) as it may change over the
years.
**Example:**
```
DEFAULT_EPICS_VERSIONS = 3.14.12 3.15.5 7.0.4.1
```
:exclamation: 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.
### `EXCLUDE_VERSIONS` Variable
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
```
### `EXCLUDE_ARCHS` Variable
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
```
### `ARCH_FILTER` Variable
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
```
:exclamation: 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`._
### Source Code and `SOURCES` Variable
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-`.
### DBD Files and `DBDS` Variable
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`.
:exclamation: There is no support for architecture dependent DBD files.
### Header Files and Dependencies, `HEADERS`, `*_VERSION`, `REQUIRED` and `IGNORE_MODULES` Variables
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`.
:exclamation: 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
```
:bulb: 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.
:bulb: 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.
:exclamation: 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
```
:exclamation: 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.
### Template Files and `TEMPLATES` Variable
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>`.
### Startup Scripts Snippets and `SCRIPTS` Variable
A module can contain startup script snippets to be executed automatically
after a module is loaded or as requested by the user.
(See [above](#startup-script-snippets).)
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](#environment-variables) 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>`.
:exclamation: 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](#local-script-variables).
### UI Screens and `QT` Variable
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/`.
:bulb: 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).
## Building EPICS 3.13 modules for 3.14 or higher
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.
### Undefined Functions
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.
### C++ Problems
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
```
### Static Device Support Structures
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`).
## Third Party Packages
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.
## Find Out Which Module Is Required
_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._