diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 6415d0a7b..df024bc7b 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -15,6 +15,26 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.
Changes between 3.15.0.1 and 3.15.0.2
+Full support for loadable support modules
+
+Apparently later versions of Base 3.14 permitted support modules to be loaded
+from a shared library at runtime without the IOC having been linked against that
+shared library; the registerRecordDeviceDriver.pl program would accept a partial
+DBD file containing just the entries needed for the library and generate the
+appropriate registration code. In 3.15 however the registerRecordDeviceDriver.pl
+program was replaced by one using the new DBD file parser, and in this a device
+support entry would only be accepted after first loading the record type that it
+depended on.
+
+The parser has been modified to accept device entries without having seen the
+record type first, although a warning is given when that happens. To remove the
+warning the DBD file can provide a record type declaration instead (no fields
+can be defined, so the braces must be empty), before the device() entry. The
+result will generate the correct registration code for the device entry without
+including anything for any merely declared record types. The generated code can
+be linked into a shared library and loaded by an IOC at runtime using dlload.
+
+
Parallel callback threads
The general purpose callback facility can run multiple parallel callback
diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c
index cf2406556..d1b3dc67e 100644
--- a/src/ioc/dbStatic/dbLexRoutines.c
+++ b/src/ioc/dbStatic/dbLexRoutines.c
@@ -62,6 +62,7 @@ static void dbMenuChoice(char *name,char *value);
static void dbMenuBody(void);
static void dbRecordtypeHead(char *name);
+static void dbRecordtypeEmpty(void);
static void dbRecordtypeBody(void);
static void dbRecordtypeFieldHead(char *name,char *type);
static void dbRecordtypeFieldItem(char *name,char *value);
@@ -607,6 +608,23 @@ static void dbRecordtypeCdef(char *text) {
return;
}
+static void dbRecordtypeEmpty(void)
+{
+ tempListNode *ptempListNode;
+ dbRecordType *pdbRecordType;
+
+ if (duplicate) {
+ duplicate = FALSE;
+ return;
+ }
+
+ ptempListNode = (tempListNode *)ellFirst(&tempList);
+ pdbRecordType = ptempListNode->item;
+ epicsPrintf("Declaration of recordtype(%s) preceeded full definition.\n",
+ pdbRecordType->name);
+ yyerrorAbort(NULL);
+}
+
static void dbRecordtypeBody(void)
{
dbRecordType *pdbRecordType;
diff --git a/src/ioc/dbStatic/dbYacc.y b/src/ioc/dbStatic/dbYacc.y
index b8cc9b4cf..858e666d3 100644
--- a/src/ioc/dbStatic/dbYacc.y
+++ b/src/ioc/dbStatic/dbYacc.y
@@ -100,7 +100,12 @@ recordtype_head: '(' tokenSTRING ')'
dbRecordtypeHead($2); dbmfFree($2);
};
-recordtype_body: '{' recordtype_field_list '}'
+recordtype_body: '{' '}'
+{
+ if(dbStaticDebug>2) printf("empty recordtype_body\n");
+ dbRecordtypeEmpty();
+}
+ | '{' recordtype_field_list '}'
{
if(dbStaticDebug>2) printf("recordtype_body\n");
dbRecordtypeBody();
@@ -215,14 +220,14 @@ record_head: '(' tokenSTRING ',' tokenSTRING ')'
dbRecordHead($2,$4,0); dbmfFree($2); dbmfFree($4);
};
-record_body: /*Null*/
+record_body: /* empty */
{
if(dbStaticDebug>2) printf("null record_body\n");
dbRecordBody();
}
| '{' '}'
{
- if(dbStaticDebug>2) printf("record_body - no fields\n");
+ if(dbStaticDebug>2) printf("empty record_body\n");
dbRecordBody();
}
| '{' record_field_list '}'
diff --git a/src/libCom/osi/os/Linux/osdFindSymbol.c b/src/libCom/osi/os/RTEMS/osdFindSymbol.c
similarity index 64%
rename from src/libCom/osi/os/Linux/osdFindSymbol.c
rename to src/libCom/osi/os/RTEMS/osdFindSymbol.c
index 967c220cd..9e947f95c 100644
--- a/src/libCom/osi/os/Linux/osdFindSymbol.c
+++ b/src/libCom/osi/os/RTEMS/osdFindSymbol.c
@@ -1,27 +1,31 @@
/*************************************************************************\
* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
+* Copyright (c) 2002 The Regents of the University of California, as
+* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* osi/os/default/epicsFindSymbol.c */
+/* osi/os/RTEMS/osdFindSymbol.c */
-#include
+/* GESYS could support this, but must use compile-time detection
+ * as the code must build for non-GESYS systems as well.
+ */
#define epicsExportSharedSymbols
#include "epicsFindSymbol.h"
epicsShareFunc void * epicsLoadLibrary(const char *name)
{
- return dlopen(name, RTLD_LAZY | RTLD_GLOBAL);
+ return 0;
}
epicsShareFunc const char *epicsLoadError(void)
{
- return dlerror();
+ return "epicsLoadLibrary not implemented";
}
epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name)
{
- return dlsym(0, name);
+ return 0;
}
diff --git a/src/libCom/osi/os/WIN32/osdFindSymbol.c b/src/libCom/osi/os/WIN32/osdFindSymbol.c
index ed2b79c95..6de3c428e 100644
--- a/src/libCom/osi/os/WIN32/osdFindSymbol.c
+++ b/src/libCom/osi/os/WIN32/osdFindSymbol.c
@@ -3,7 +3,7 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* osi/os/WIN32/epicsFindSymbol.c */
+/* osi/os/WIN32/osdFindSymbol.c */
#include
diff --git a/src/libCom/osi/os/default/osdFindSymbol.c b/src/libCom/osi/os/default/osdFindSymbol.c
index 99ca2832d..9d9a8878c 100644
--- a/src/libCom/osi/os/default/osdFindSymbol.c
+++ b/src/libCom/osi/os/default/osdFindSymbol.c
@@ -6,16 +6,22 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* osi/os/default/epicsFindSymbol.c */
+/* osi/os/default/osdFindSymbol.c */
#define epicsExportSharedSymbols
#include "epicsFindSymbol.h"
epicsShareFunc void * epicsLoadLibrary(const char *name)
-{ return 0; }
+{
+ return 0;
+}
epicsShareFunc const char *epicsLoadError(void)
-{ return "epicsLoadLibrary not implemented"; }
+{
+ return "epicsLoadLibrary not implemented";
+}
epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name)
-{ return 0;}
+{
+ return 0;
+}
diff --git a/src/libCom/osi/os/Darwin/osdFindSymbol.c b/src/libCom/osi/os/posix/osdFindSymbol.c
similarity index 95%
rename from src/libCom/osi/os/Darwin/osdFindSymbol.c
rename to src/libCom/osi/os/posix/osdFindSymbol.c
index 967c220cd..acfc405e5 100644
--- a/src/libCom/osi/os/Darwin/osdFindSymbol.c
+++ b/src/libCom/osi/os/posix/osdFindSymbol.c
@@ -4,7 +4,7 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* osi/os/default/epicsFindSymbol.c */
+/* osi/os/posix/osdFindSymbol.c */
#include
diff --git a/src/libCom/osi/os/solaris/osdFindSymbol.c b/src/libCom/osi/os/solaris/osdFindSymbol.c
deleted file mode 100644
index 967c220cd..000000000
--- a/src/libCom/osi/os/solaris/osdFindSymbol.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*************************************************************************\
-* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
-* National Laboratory.
-* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
-\*************************************************************************/
-/* osi/os/default/epicsFindSymbol.c */
-
-#include
-
-#define epicsExportSharedSymbols
-#include "epicsFindSymbol.h"
-
-epicsShareFunc void * epicsLoadLibrary(const char *name)
-{
- return dlopen(name, RTLD_LAZY | RTLD_GLOBAL);
-}
-
-epicsShareFunc const char *epicsLoadError(void)
-{
- return dlerror();
-}
-
-epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name)
-{
- return dlsym(0, name);
-}
diff --git a/src/libCom/osi/os/vxWorks/osdFindSymbol.c b/src/libCom/osi/os/vxWorks/osdFindSymbol.c
index d9c9d1161..785327e8b 100644
--- a/src/libCom/osi/os/vxWorks/osdFindSymbol.c
+++ b/src/libCom/osi/os/vxWorks/osdFindSymbol.c
@@ -6,7 +6,7 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* osi/os/vxWorks/osiFindSymbol */
+/* osi/os/vxWorks/osdFindSymbol */
/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */
#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h>
diff --git a/src/tools/DBD/Parser.pm b/src/tools/DBD/Parser.pm
index 8cace9c17..b8cc82783 100644
--- a/src/tools/DBD/Parser.pm
+++ b/src/tools/DBD/Parser.pm
@@ -59,9 +59,12 @@ sub ParseDBD {
\s* $RXstr \s* , \s*$RXstr \s* \)/oxgc) {
print "Device: $1, $2, $3, $4\n" if $debug;
my $rtyp = $dbd->recordtype($1);
- dieContext("Unknown record type '$1'")
- unless defined $rtyp;
- $rtyp->add_device(DBD::Device->new($2, $3, $4));
+ if (!defined $rtyp) {
+ $rtyp = DBD::Recordtype->new($1);
+ warn "Device using undefined record type '$1', place-holder created\n";
+ $dbd->add($rtyp);
+ }
+ $rtyp->add_device(DBD::Device->new($2, $3, $4));
} else {
last unless m/\G (.*) $/moxgc;
dieContext("Syntax error in '$1'");
diff --git a/src/tools/DBD/Recordtype.pm b/src/tools/DBD/Recordtype.pm
index f730777a3..d0d76a5a0 100644
--- a/src/tools/DBD/Recordtype.pm
+++ b/src/tools/DBD/Recordtype.pm
@@ -83,7 +83,9 @@ sub toCdefs {
}
sub equals {
- my ($a, $b) = @_;
+ my ($new, $known) = @_;
+ return 0 if ! $known->fields;
+ return 1 if ! $new->fields;
dieContext("Duplicate definition of record type '$a->{NAME}'");
}
diff --git a/src/tools/registerRecordDeviceDriver.pl b/src/tools/registerRecordDeviceDriver.pl
index 91a22c903..368d7acea 100644
--- a/src/tools/registerRecordDeviceDriver.pl
+++ b/src/tools/registerRecordDeviceDriver.pl
@@ -20,10 +20,10 @@ use EPICS::Path;
use EPICS::Getopts;
use Text::Wrap;
-our ($opt_D, @opt_I, $opt_o);
+our ($opt_D, @opt_I, $opt_o, $opt_l);
-getopts('Do:I@') or
- die "Usage: registerRecordDeviceDriver [-D] [-o out.c] [-I dir] in.dbd subname [TOP]";
+getopts('Dlo:I@') or
+ die "Usage: registerRecordDeviceDriver [-D] [-l] [-o out.c] [-I dir] in.dbd subname [TOP]";
my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32?
@@ -69,37 +69,52 @@ print $out (<< "END");
#include "iocshRegisterCommon.h"
#include "registryCommon.h"
+END
+
+print $out (<< "END") if $opt_l;
+#define epicsExportSharedSymbols
+#include "shareLib.h"
+
+END
+
+print $out (<< "END");
extern "C" {
END
my %rectypes = %{$dbd->recordtypes};
+my @rtypnames;
my @dsets;
if (%rectypes) {
- my @rtypnames = sort keys %rectypes;
+ my @allrtypnames = sort keys %rectypes;
+ # Record types with no fields defined are declarations,
+ # for building shared libraries containing device support.
+ @rtypnames = grep { scalar $rectypes{$_}->fields } @allrtypnames;
- # Declare the record support entry tables
- print $out wrap('epicsShareExtern rset ', ' ',
- join(', ', map {"*pvar_rset_${_}RSET"} @rtypnames)), ";\n\n";
+ if (@rtypnames) {
+ # Declare the record support entry tables
+ print $out wrap('epicsShareExtern rset ', ' ',
+ join(', ', map {"*pvar_rset_${_}RSET"} @rtypnames)), ";\n\n";
- # Declare the RecordSizeOffset functions
- print $out "typedef int (*rso_func)(dbRecordType *pdbRecordType);\n";
- print $out wrap('epicsShareExtern rso_func ', ' ',
- join(', ', map {"pvar_func_${_}RecordSizeOffset"} @rtypnames)), ";\n\n";
+ # Declare the RecordSizeOffset functions
+ print $out "typedef int (*rso_func)(dbRecordType *pdbRecordType);\n";
+ print $out wrap('epicsShareExtern rso_func ', ' ',
+ join(', ', map {"pvar_func_${_}RecordSizeOffset"} @rtypnames)), ";\n\n";
- # List of record type names
- print $out "static const char * const recordTypeNames[] = {\n";
- print $out wrap(' ', ' ', join(', ', map {"\"$_\""} @rtypnames));
- print $out "\n};\n\n";
+ # List of record type names
+ print $out "static const char * const recordTypeNames[] = {\n";
+ print $out wrap(' ', ' ', join(', ', map {"\"$_\""} @rtypnames));
+ print $out "\n};\n\n";
- # List of pointers to each RSET and RecordSizeOffset function
- print $out "static const recordTypeLocation rtl[] = {\n";
- print $out join(",\n", map {
- " {pvar_rset_${_}RSET, pvar_func_${_}RecordSizeOffset}"
- } @rtypnames);
- print $out "\n};\n\n";
+ # List of pointers to each RSET and RecordSizeOffset function
+ print $out "static const recordTypeLocation rtl[] = {\n";
+ print $out join(",\n", map {
+ " {pvar_rset_${_}RSET, pvar_func_${_}RecordSizeOffset}"
+ } @rtypnames);
+ print $out "\n};\n\n";
+ }
- for my $rtype (@rtypnames) {
+ for my $rtype (@allrtypnames) {
my @devices = $rectypes{$rtype}->devices;
for my $dtype (@devices) {
my $dset = $dtype->name;
@@ -206,7 +221,7 @@ print $out (<< 'END');
END
-print $out (<< 'END') if %rectypes;
+print $out (<< 'END') if %rectypes && @rtypnames;
registerRecordTypes(pbase, NELEMENTS(rtl), recordTypeNames, rtl);
END