From ee40ee789c5e4d8bd92cc8baa28484480aafd44f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 Jul 2014 23:26:01 -0500 Subject: [PATCH 1/7] dbStatic: Allow empty recordtype bodies in DBD files A record type with an empty body is a declaration. The IOC will accept one of these in a DBD file as long as it has already loaded the full record type definition. This will allow device support to provide a DBD file that declares each record type it uses before giving the related device() entry. Later commits will explain why. --- src/ioc/dbStatic/dbLexRoutines.c | 18 ++++++++++++++++++ src/ioc/dbStatic/dbYacc.y | 11 ++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) 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 '}' From 8eed4cdd8812dffe77b82501dc08e1ef96b32275 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 Jul 2014 23:37:58 -0500 Subject: [PATCH 2/7] Perl DBD Parser: Autovivify record types in device() entries If a record type named in a device() entry does not exist when the device is seen, the DBD parser will now print a warning and create an empty record type with that name to hold the device entry information. This will allow support modules to be built into libraries that include their own registerRecordDeviceDriver code, which was apparently possible with 3.14 although not officially supported. To avoid the warning message, the DBD file can declare each record type before the device statement that uses it. A record type declaration looks like this: recordtype(ao) {} device(ao, ....) An IOC will accept and ignore a record type declaration, but it must have loaded the full record type definition first. --- src/tools/DBD/Parser.pm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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'"); From a2d511b6e9ac450014263688d234f8f7429a7471 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 Jul 2014 23:55:47 -0500 Subject: [PATCH 3/7] DBD parser accepts record type declarations Record types are the only DBD entries for which the parser will not accept redefinitions. This change lets it accept a record type declaration any number of times. However once a record type declaration has been parsed the full record type definition cannot be loaded, an error will be raised if the full definition is seen. The parser will still not accept a full record type definition more than once either, any later copies of the record type must be declarations. --- src/tools/DBD/Recordtype.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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}'"); } From 163cf7971df18d21c54c5852d8771486a7e5aa33 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 28 Jul 2014 00:16:16 -0500 Subject: [PATCH 4/7] Errors from the DBD Parser should stop dbdExpand Such errors used to be just warnings, now they stop the output file being generated unless the -D flag was given. --- src/tools/dbdExpand.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/dbdExpand.pl b/src/tools/dbdExpand.pl index c9e10aa51..0157c6b84 100755 --- a/src/tools/dbdExpand.pl +++ b/src/tools/dbdExpand.pl @@ -44,6 +44,7 @@ while (@ARGV) { warn " Your Makefile may need this dependency rule:\n" . " $dep: \$(COMMON_DIR)/$file\n" if $@ =~ m/Can't find file/; + exit 1 unless $opt_D; } } From 4b1fd8cba0fd63addbf80d3f1baa432b74b16b14 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 28 Jul 2014 00:18:37 -0500 Subject: [PATCH 5/7] Modify registerRecordDeviceDriver for loadable support Don't generate code to register record types that have only been declared, i.e. that have no fields defined in them. This allows libraries to created which only contain device support without the record types that they support. The library must have a DBD file that may declare the record types (to avoid generating a warning) and also contains any other device(), variable(), function() and registrar() entries for software included in the library. Adds -l flag to the registerRecordDeviceDriver program, which results in epicsExportSharedSymbols being defined and shareLib.h being reloaded so the generated output file can be linked into the library that it is registering. This aspect has not been tested on Windows DLLs yet. --- src/tools/registerRecordDeviceDriver.pl | 61 +++++++++++++++---------- 1 file changed, 38 insertions(+), 23 deletions(-) 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 From 5708855c363db4430eb6e5a1bdb6f503d7143762 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 28 Jul 2014 00:52:34 -0500 Subject: [PATCH 6/7] Release Notes for loading support. To Do: * Test that -l the flag to registerRecordDeviceDriver.pl works as expected on Windows. * Modify example template, or create a new template. * Work out if/how this affects VxWorks, can we generate a munch file that works the same way as a shared library? * Test on all arch's, especially Darwin & Windows. --- documentation/RELEASE_NOTES.html | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 4e4845715..38096843c 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. +

+

Database field setting updates

A database (.db) file loaded by an IOC does not have to repeat the record From 75da9fd4542d37d87313dd223340a136d37562a9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 28 Jul 2014 12:27:02 -0500 Subject: [PATCH 7/7] Moved dlopen version of osdFindSymbol.c into posix It was identical in solaris, Darwin and Linux, and had not been included for cygwin so this reduces duplication. --- src/libCom/osi/os/Linux/osdFindSymbol.c | 27 ------------------- .../osi/os/{Darwin => posix}/osdFindSymbol.c | 2 +- src/libCom/osi/os/solaris/osdFindSymbol.c | 27 ------------------- 3 files changed, 1 insertion(+), 55 deletions(-) delete mode 100644 src/libCom/osi/os/Linux/osdFindSymbol.c rename src/libCom/osi/os/{Darwin => posix}/osdFindSymbol.c (95%) delete mode 100644 src/libCom/osi/os/solaris/osdFindSymbol.c diff --git a/src/libCom/osi/os/Linux/osdFindSymbol.c b/src/libCom/osi/os/Linux/osdFindSymbol.c deleted file mode 100644 index 967c220cd..000000000 --- a/src/libCom/osi/os/Linux/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/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..fd81f49e3 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/epicsFindSymbol.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); -}