311 lines
8.6 KiB
Perl
311 lines
8.6 KiB
Perl
#!/usr/bin/env perl
|
|
|
|
#*************************************************************************
|
|
# Copyright (c) 2012 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.
|
|
# SPDX-License-Identifier: EPICS
|
|
# EPICS BASE is distributed subject to a Software License Agreement found
|
|
# in file LICENSE that is included with this distribution.
|
|
#*************************************************************************
|
|
|
|
use strict;
|
|
|
|
use FindBin qw($Bin);
|
|
use lib ("$Bin/../../lib/perl");
|
|
|
|
use DBD;
|
|
use DBD::Parser;
|
|
use EPICS::Readfile;
|
|
use EPICS::Path;
|
|
use EPICS::Getopts;
|
|
use Text::Wrap;
|
|
|
|
our ($opt_D, @opt_I, $opt_o, $opt_l);
|
|
|
|
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?
|
|
|
|
my ($file, $subname, $bldTop) = @ARGV;
|
|
|
|
# Auto-declaration of record types is needed to build loadable modules
|
|
$DBD::Parser::allowAutoDeclarations = 1;
|
|
|
|
my $dbd = DBD->new();
|
|
ParseDBD($dbd, Readfile($file, "", \@path));
|
|
|
|
if ($opt_D) { # Output dependencies only
|
|
my %filecount;
|
|
my @uniqfiles = grep { not $filecount{$_}++ } @inputfiles;
|
|
print "$opt_o: ", join(" \\\n ", @uniqfiles), "\n\n";
|
|
print map { "$_:\n" } @uniqfiles;
|
|
exit 0;
|
|
}
|
|
|
|
$Text::Wrap::columns = 75;
|
|
|
|
# Eliminate chars not allowed in C symbol names
|
|
my $c_bad_ident_chars = '[^0-9A-Za-z_]';
|
|
$subname =~ s/$c_bad_ident_chars/_/g;
|
|
|
|
# Process bldTop like convertRelease.pl does
|
|
$bldTop = LocalPath(UnixPath($bldTop));
|
|
$bldTop =~ s/([\\"])/\\\1/g; # escape back-slashes and double-quotes
|
|
|
|
# Create output file
|
|
my $out;
|
|
if ($opt_o) {
|
|
open $out, '>', $opt_o or die "Can't create $opt_o: $!\n";
|
|
} else {
|
|
$out = *STDOUT;
|
|
}
|
|
|
|
print $out (<< "END");
|
|
/* THIS IS A GENERATED FILE. DO NOT EDIT! */
|
|
/* Generated from $file */
|
|
|
|
#include <string.h>
|
|
#ifndef USE_TYPED_RSET
|
|
# define USE_TYPED_RSET
|
|
#endif
|
|
#include "compilerDependencies.h"
|
|
#include "epicsStdlib.h"
|
|
#include "iocsh.h"
|
|
#include "iocshRegisterCommon.h"
|
|
#include "registryCommon.h"
|
|
#include "recSup.h"
|
|
#include "shareLib.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 @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;
|
|
|
|
if (@rtypnames) {
|
|
# Declare the record support entry tables
|
|
print $out wrap('epicsShareExtern typed_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";
|
|
|
|
# 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 {
|
|
" {(struct typed_rset *)pvar_rset_${_}RSET, pvar_func_${_}RecordSizeOffset}"
|
|
} @rtypnames);
|
|
print $out "\n};\n\n";
|
|
}
|
|
|
|
for my $rtype (@allrtypnames) {
|
|
my @devices = $rectypes{$rtype}->devices;
|
|
for my $dtype (@devices) {
|
|
my $dset = $dtype->name;
|
|
push @dsets, $dset;
|
|
}
|
|
}
|
|
|
|
if (@dsets) {
|
|
# Declare the device support entry tables
|
|
print $out wrap('epicsShareExtern dset ', ' ',
|
|
join(', ', map {"*pvar_dset_$_"} @dsets)), ";\n\n";
|
|
|
|
# List of dset names
|
|
print $out "static const char * const deviceSupportNames[] = {\n";
|
|
print $out wrap(' ', ' ', join(', ', map {"\"$_\""} @dsets));
|
|
print $out "\n};\n\n";
|
|
|
|
# List of pointers to each dset
|
|
print $out "static const dset * const devsl[] = {\n";
|
|
print $out wrap(' ', ' ', join(", ", map {"pvar_dset_$_"} @dsets));
|
|
print $out "\n};\n\n";
|
|
}
|
|
}
|
|
|
|
my %drivers = %{$dbd->drivers};
|
|
if (%drivers) {
|
|
my @drivers = sort keys %drivers;
|
|
|
|
# Declare the driver entry tables
|
|
print $out wrap('epicsShareExtern drvet ', ' ',
|
|
join(', ', map {"*pvar_drvet_$_"} @drivers)), ";\n\n";
|
|
|
|
# List of drvet names
|
|
print $out "static const char *driverSupportNames[] = {\n";
|
|
print $out wrap(' ', ' ', join(', ', map {"\"$_\""} @drivers));
|
|
print $out "};\n\n";
|
|
|
|
# List of pointers to each drvet
|
|
print $out "static struct drvet *drvsl[] = {\n";
|
|
print $out join(",\n", map {" pvar_drvet_$_"} @drivers);
|
|
print $out "};\n\n";
|
|
}
|
|
|
|
my %links = %{$dbd->links};
|
|
if (%links) {
|
|
my @links = sort keys %links;
|
|
|
|
# Declare the link interfaces
|
|
print $out wrap('epicsShareExtern jlif ', ' ',
|
|
join(', ', map {"*pvar_jlif_$_"} @links)), ";\n\n";
|
|
|
|
# List of pointers to each link interface
|
|
print $out "static struct jlif *jlifsl[] = {\n";
|
|
print $out join(",\n", map {" pvar_jlif_$_"} @links);
|
|
print $out "};\n\n";
|
|
}
|
|
|
|
my @registrars = sort keys %{$dbd->registrars};
|
|
my @functions = sort keys %{$dbd->functions};
|
|
push @registrars, map {"register_func_$_"} @functions;
|
|
if (@registrars) {
|
|
# Declare the registrar functions
|
|
print $out "typedef void (*reg_func)(void);\n";
|
|
print $out wrap('epicsShareExtern reg_func ', ' ',
|
|
join(', ', map {"pvar_func_$_"} @registrars)), ";\n\n";
|
|
}
|
|
|
|
my %variables = %{$dbd->variables};
|
|
if (%variables) {
|
|
my @varnames = sort keys %variables;
|
|
|
|
# Declare the variables
|
|
for my $var (@varnames) {
|
|
my $vtype = $variables{$var}->var_type;
|
|
print $out "epicsShareExtern $vtype * const pvar_${vtype}_$var;\n";
|
|
}
|
|
|
|
# Generate the structure for registering variables with iocsh
|
|
print $out "\nstatic struct iocshVarDef vardefs[] = {\n";
|
|
for my $var (@varnames) {
|
|
my $vtype = $variables{$var}->var_type;
|
|
my $itype = $variables{$var}->iocshArg_type;
|
|
print $out " {\"$var\", $itype, pvar_${vtype}_$var},\n";
|
|
}
|
|
print $out " {NULL, iocshArgInt, NULL}\n};\n\n";
|
|
}
|
|
|
|
# Now for actual registration routine
|
|
|
|
print $out (<< "END");
|
|
int $subname(DBBASE *pbase)
|
|
{
|
|
static int executed = 0;
|
|
END
|
|
|
|
print $out (<< "END") if $bldTop ne '';
|
|
const char *bldTop = "$bldTop";
|
|
const char *envTop = getenv("TOP");
|
|
|
|
if (envTop && strcmp(envTop, bldTop)) {
|
|
printf("Warning: IOC is booting with TOP = \\"%s\\"\\n"
|
|
" but was built with TOP = \\"%s\\"\\n",
|
|
envTop, bldTop);
|
|
}
|
|
|
|
END
|
|
|
|
print $out (<< 'END');
|
|
if (!pbase) {
|
|
printf("pdbbase is NULL; you must load a DBD file first.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (executed) {
|
|
printf("Warning: Registration already done.\n");
|
|
}
|
|
executed = 1;
|
|
|
|
END
|
|
|
|
print $out (<< 'END') if %rectypes && @rtypnames;
|
|
registerRecordTypes(pbase, NELEMENTS(rtl), recordTypeNames, rtl);
|
|
END
|
|
|
|
print $out (<< 'END') if @dsets;
|
|
registerDevices(pbase, NELEMENTS(devsl), deviceSupportNames, devsl);
|
|
END
|
|
|
|
print $out (<< 'END') if %drivers;
|
|
registerDrivers(pbase, NELEMENTS(drvsl), driverSupportNames, drvsl);
|
|
END
|
|
|
|
print $out (<< 'END') if %links;
|
|
registerJLinks(pbase, NELEMENTS(jlifsl), jlifsl);
|
|
END
|
|
|
|
print $out (<< "END") for @registrars;
|
|
runRegistrarOnce(pvar_func_$_);
|
|
END
|
|
|
|
print $out (<< 'END') if %variables;
|
|
iocshRegisterVariable(vardefs);
|
|
END
|
|
|
|
print $out (<< "END");
|
|
return 0;
|
|
}
|
|
|
|
/* $subname */
|
|
static const iocshArg rrddArg0 = {"pdbbase", iocshArgPdbbase};
|
|
static const iocshArg *rrddArgs[] = {&rrddArg0};
|
|
static const iocshFuncDef rrddFuncDef = {
|
|
"$subname",
|
|
1,
|
|
rrddArgs,
|
|
"Register the various records, devices, for this DBD.\\n\\n"
|
|
"These are registered into the database given as first argument,\\n"
|
|
"which should always be 'pdbbase'.\\n\\n"
|
|
"Example: $subname pdbbase\\n",
|
|
};
|
|
static void rrddCallFunc(const iocshArgBuf *)
|
|
{
|
|
iocshSetError($subname(*iocshPpdbbase));
|
|
}
|
|
|
|
} // extern "C"
|
|
|
|
/*
|
|
* Register commands on application startup
|
|
*/
|
|
static int Registration() {
|
|
iocshRegisterCommon();
|
|
iocshRegister(&rrddFuncDef, rrddCallFunc);
|
|
return 0;
|
|
}
|
|
|
|
static int done EPICS_UNUSED = Registration();
|
|
END
|
|
|
|
if ($opt_o) {
|
|
close $out or die "Closing $opt_o failed: $!\n";
|
|
}
|
|
exit 0;
|