OpenFPGA/libs/EXTERNAL/tcl8.6.12/pkgs/tdbc1.1.3/tools/genExtStubs.tcl

352 lines
8.9 KiB
Tcl

# genExtStubs.tcl --
#
# Generates an import table for one or more external dynamic
# link libraries.
#
# Usage:
#
# tclsh genExtStubs.tcl stubDefs.txt stubStruct.h stubInit.c
#
# Parameters:
#
# stubsDefs.txt --
# Name of a file containing declarations of functions
# to be stubbed. The functions are expected to be in
# stylized C where exach appears on a single line, and
# has the form 'returnType name(param,param,...);'
# In addition, comments of the following forms
# are expected to precede the function declarations.
# /* LIBRARY: name1 name2... */
# These comments give the rootnames of dynamic link
# libraries that are expected to contain the functions,
# in order of preference.
# /* STUBSTRUCT: prefix */
# String to be prepended to the function name that translates
# to its reference in the stub table.
# stubStruct.h --
# Name of a file that will contain (a) the declaration
# of a structure that contains pointers to the stubbed
# functions, and (b) #defines replacing the function name
# with references into the stub table
# parseImports --
#
# Parse the import declarations in a given file
#
# Parameters:
# stubDefs -- Name of the file to parse
#
# Results:
#
# Returns a list of tuples. The possible tuples are:
#
# libraries NAME NAME...
# Sets the names of the
# prefix NAME
# Sets the name of the stub structure to NAME and prefixes
# all the definitions of the stubbed routines with NAME
# import TYPE NAME PARAMS
# Declares the imported routine NAME to return data of type
# TYPE and accept parmeters PARAMS.
proc parseImports {stubDefs} {
set defsFile [open $stubDefs r]
set imports {}
set lineNo 0
while {[gets $defsFile line] >= 0} {
incr lineNo
if {[string is space $line]} {
# do nothing
} elseif {[regexp -expanded -- {
^\s*\*\s*LIBRARY:\s+
([a-zA-Z0-9_]+(?:\s+[a-zA-Z0-9_]+)*) # List of library names
} $line -> m]} {
set libNames $m
lappend imports [linsert $libNames 0 libraries]
} elseif {[regexp {^\s*\*\s*STUBSTRUCT:\s*(.*)} $line -> m]} {
set stubPrefix $m
lappend imports [list prefix $m]
} elseif {[regexp {^\s*\*\s*CONVENTION:\s*(.*)} $line -> c]} {
lappend imports [list convention $c]
} elseif {[regexp -nocase -- {^\s*#} $line]} {
# do nothing
} elseif {[regexp -nocase -expanded -- {
\s*(.*)\s+ # Return type
([[:alpha:]_][[:alnum:]_]+) # Function name
\s*\((.*)\); # Parameters
} $line -> type name params]} {
lappend imports [list import $type $name $params]
} else {
puts stderr "$stubDefs:$lineNo: unrecognized syntax"
}
}
close $defsFile
return $imports
}
# writeStructHeader --
#
# Writes the header of the stubs structure to the '.h' file
#
# Parameters:
# stubDefs -- Name of the input file from which stubs are being
# generated
# stubStruct -- Name of the file .h being written
# structFile -- Channel ID of the .h file being written
#
# Results:
# None.
#
# Side effects:
# Writes the 'struct' header to the .h file
proc writeStructHeader {stubDefs stubStruct structFile} {
chan puts $structFile "/*"
chan puts $structFile " *[string repeat - 77]"
chan puts $structFile " *"
chan puts $structFile " * $stubStruct --"
chan puts $structFile " *"
chan puts $structFile " *\tStubs for procedures in [file tail $stubDefs]"
chan puts $structFile " *"
chan puts $structFile " * Generated by [file tail $::argv0]: DO NOT EDIT"
chan puts $structFile " * [clock format [clock seconds] \
-format {%Y-%m-%d %H:%M:%SZ} -gmt true]"
chan puts $structFile " *"
chan puts $structFile " *[string repeat - 77]"
chan puts $structFile " */"
chan puts $structFile ""
chan puts $structFile "typedef struct [file rootname [file tail $stubDefs]] \{"
return
}
# writeStubDeclarations --
#
# Writes the declarations of the stubs in the table to the .h file.
#
# Parameters:
# structFile -- Channel ID of the .h file
# imports -- List of tuples returned from 'parseImports'
#
# Results:
# None.
#
# Side effects:
# C pointer-to-function declarations are written to the given file.
proc writeStubDeclarations {structFile imports} {
set convention {}
foreach i $imports {
set key [lindex $i 0]
switch -exact -- $key {
convention {
set convention [lindex $i 1]
}
import {
lassign $i key type name params
chan puts $structFile \
" $type (${convention}*${name}Ptr)($params);"
}
libraries {
chan puts $structFile {}
chan puts $structFile \
" /* Functions from libraries: [lrange $i 1 end] */"
chan puts $structFile {}
}
default {
}
}
}
return
}
# writeStructFooter --
#
# Writes the close of the 'struct' declaration to the .h file
#
# Parameters:
# stubDefs -- Name of the struct
# structFile -- Channel handle of the .h file
#
# Results:
# None
#
# Side effects:
# Structure declaration is closed.
proc writeStructFooter {stubDefs structFile} {
chan puts $structFile "\} [file rootname [file tail $stubDefs]]\;"
return
}
# writeStubDefines --
#
# Write the #define directives that replace stub function calls with
# indirections through the stubs table.
#
# Parameters:
# structFile -- Channel id of the .h file
# imports -- Table of imports from parseImports
proc writeStubDefines {structFile imports} {
set stubPrefix {}
foreach i $imports {
switch -exact -- [lindex $i 0] {
prefix {
lassign $i -> stubPrefix
}
import {
lassign $i -> type name params
chan puts $structFile "#define $name ($stubPrefix->${name}Ptr)"
}
}
}
return $stubPrefix
}
# accumulateLibNames --
#
# Accumulates the list of library names into the Stub initialization
#
# Parameters:
# codeVar - Name of variable in caller's scope containing the code
# under construction
# imports - Import definitions from 'parseImports'
#
# Results:
# Returns the code burst for the initialization file.
proc accumulateLibNames {codeVar imports} {
upvar 1 $codeVar code
set sep "\n "
foreach i $imports {
if {[lindex $i 0] eq {libraries}} {
foreach lib [lrange $i 1 end] {
append code $sep \" $lib \"
set sep ", "
}
}
}
append code $sep "NULL"
}
# accumulateSymNames --
#
# Accumulates the list of import symbols into the Stub initialization
#
# Parameters:
# codeVar - Name of variable in caller's scope containing the code
# under construction
# imports - Import definitions from 'parseImports'
#
# Results:
# Returns the code burst for the initialization file.
proc accumulateSymNames {codeVar imports} {
upvar 1 $codeVar code
set inLibrary 0
set sep {}
foreach i $imports {
switch -exact -- [lindex $i 0] {
import {
lassign $i key type name args
append code $sep \n { } \" $name \"
set sep ,
}
}
}
append code $sep \n { NULL}
}
# rewriteInitProgram --
#
# Rewrite the 'stubInit.c' program to contain new definitions
# of imported routines
#
# Parameters:
# oldProgram -- Previous content of the 'stubInit.c' file
# imports -- Import definitions from 'parseImports'
#
# Results:
# Returns the new import program
proc rewriteInitProgram {stubDefs oldProgram imports} {
set newProgram {}
set sep {}
set state {}
foreach piece [split $oldProgram \n] {
switch -exact -- $state {
{} {
switch -regexp -- $piece {
@CREATED@ {
regsub @CREATED@.* $piece {@CREATED@ } piece
append piece [clock format [clock seconds] \
-format {%Y-%m-%d %H:%M:%SZ} \
-gmt 1]
append piece " by " [file tail $::argv0]
append piece " from " $stubDefs
}
@LIBNAMES@ {
set state ignoring
accumulateLibNames piece $imports
}
@SYMNAMES@ {
set state ignoring
accumulateSymNames piece $imports
}
}
append newProgram $sep $piece
set sep \n
}
ignoring {
if {[regexp -- @END@ $piece]} {
set state {}
append newProgram $sep $piece
set sep \n
}
}
}
}
return $newProgram
}
# MAIN PROGRAM - see file header for calling sequence
proc main {stubDefs stubStruct stubInit} {
# Parse the import definitions
set imports [parseImports $stubDefs]
# Write the Stub structure declarations
set structFile [open $stubStruct w]
chan configure $structFile -translation lf
writeStructHeader $stubDefs $stubStruct $structFile
writeStubDeclarations $structFile $imports
writeStructFooter $stubDefs $structFile
set stubPrefix [writeStubDefines $structFile $imports]
chan puts $structFile "MODULE_SCOPE const [file rootname [file tail $stubDefs]]\
*${stubPrefix};"
close $structFile
# Write the initializations of the function names to import
set initFile [open $stubInit r+]
set initProgram [chan read $initFile]
set initProgram [rewriteInitProgram $stubDefs $initProgram $imports]
chan seek $initFile 0
chan truncate $initFile
chan configure $initFile -translation lf
chan puts -nonewline $initFile $initProgram
close $initFile
}
main {*}$argv