Skip to content

Canadian-Light-Source/require

Β 
Β 

Repository files navigation

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. 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 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).
  • $($(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). 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.

❗ 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.

πŸ’‘ 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.

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). 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!

❗ 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.

πŸ’‘ 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 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.

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 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.

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

❗ 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.

❗ 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.

❗ 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.

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.)

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.

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/.

πŸ’‘ 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.

About

PSI dynamic modules for EPICS

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C 48.0%
  • Makefile 26.5%
  • Shell 9.6%
  • Yacc 8.4%
  • Perl 6.3%
  • Lex 0.8%
  • Scheme 0.4%