#!/usr/bin/env python
#
# Copyright (c) 2008-2011,2013 Wind River Systems, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the Lesser GNU General Public License version 2.1 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# version 2.1 along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""convert wrapfuncs.in to wrapper function stubs and tables"""
import datetime
import glob
import sys
import re
import os.path
import string
import subprocess
from templatefile import TemplateFile
class ArgumentList:
"""A (possibly empty) list of arguments"""
def __init__(self, text):
"parse a comma-separated argument list (including function prototypes)"
self.args = []
self.variadic = False
self.variadic_decl = ""
self.variadic_start = ""
self.variadic_end = ""
# (void) is an empty list, not a list of a single argument which is void
if text == "void":
return
depth = 0
accum = ''
comma_sep = text.split(', ')
# now, what if there was a comma embedded in an argument?
for arg in comma_sep:
lcount = arg.count('(')
rcount = arg.count(')')
depth = depth + lcount - rcount
if (depth > 0):
accum += arg + ', '
else:
self.args.append(Argument(accum + arg))
accum = ''
if depth != 0:
raise Exception("mismatched ()s while parsing '%s'" % text)
if self.args[-1].vararg:
self.variadic = True
self.variadic_arg = self.args[-1]
self.last_fixed_arg = self.args[-2].name
self.variadic_decl = "va_list ap;\n"
self.variadic_start = "va_start(ap, %s);\n" % self.last_fixed_arg
if self.variadic_arg.vararg_wraps:
self.variadic_decl += "\t%s;\n" % \
self.variadic_arg.vararg_wraps.decl()
self.variadic_start += ("\t%s = va_arg(ap, %s);"
"\n\tva_end(ap);\n") % \
(self.variadic_arg.name,
self.variadic_arg.type)
else:
# lie blatantly; we don't handle this case
self.variadic = False
# for a wrap function, the outer foo() wrapper will convert to a va_list,
# but the inner wrap_foo() just passes the va_list through.
def maybe_variadic_start(self):
"""Use va_arg() to grab an optional argument if needed."""
if self.variadic and self.variadic_arg.vararg_wraps:
return self.variadic_start
else:
return ""
def maybe_variadic_decl(self):
"""Declare va_list ap and optional argument, if needed."""
if self.variadic and self.variadic_arg.vararg_wraps:
return self.variadic_decl
else:
return ""
def decl(self, comment=False, wrap=False):
"""Produce the declaration form of this argument list."""
if not self.args:
return "void"
return ', '.join(x.decl(comment=comment, wrap=wrap) for x in self.args)
def call(self):
"""Produce the calling form of this argument list."""
if not self.args:
return ""
return ', '.join([x.call() for x in self.args])
def __repr__(self):
if not self.args:
return "no arguments"
else:
return '::'.join([x.decl() for x in self.args])
class Argument:
"""A function argument such as 'char *path' or 'char (*foo)(void)'"""
def __init__(self, text):
"""get the type and name of a trivial C declaration"""
self.vararg = False
self.function_pointer = False
self.spacer = ''
if text == 'void':
raise Exception("Tried to declare a nameless object of type void.")
if text.startswith('...'):
self.vararg = True
if len(text) > 3:
# we're a wrapper for something else, declared as
# ...{real_decl}, as in the third argument to open(2)
text = text[4:-1]
# stash a copy of these values without the vararg flag, so
# we can declare them prettily later
self.vararg_wraps = Argument(text)
else:
# nothing to do.
self.vararg_wraps = None
self.type, self.name = None, None
return
else:
self.vararg = False
# try for a function pointer
match = re.match('(.*)\(\*([a-zA-Z0-9$_]*)\)\((.*)\)', text)
if match:
self.function_pointer = True
self.args = match.group(3)
self.type = match.group(1)
self.name = match.group(2).rstrip()
else:
# plain declaration
match = re.match('(.*[ *])\(?\*?([a-zA-Z0-9$_]*)\)?', text)
# there may not be a match, say in the special case
# where an arg is '...'
if match:
self.type, self.name = match.group(1).rstrip(), match.group(2)
else:
self.type, self.name = None, None
# spacing between type and name, needed if type ends with a character
# which could be part of an identifier
if re.match('[_a-zA-Z0-9]', self.type[-1]):
self.spacer = ' '
def decl(self, comment=False, wrap=False):
"""Produce the declaration form of this argument."""
if self.function_pointer:
decl = "%s%s(*%s)(%s)" % \
(self.type, self.spacer, self.name, self.args)
else:
decl = "%s%s%s" % (self.type, self.spacer, self.name)
if self.vararg:
if self.vararg_wraps:
if comment:
decl = "... { %s }" % decl
else:
decl = "... /* %s */" % decl
else:
if wrap:
decl = "va_list ap"
else:
decl = "..."
return decl
def call(self):
"""Produce the call form of this argument (usually its name)."""
if self.type == 'void':
return ''
if self.vararg and not self.vararg_wraps:
return "ap"
return self.name
def __str__(self):
return self.decl()
def __repr__(self):
return self.decl()
typedata = {
'char *': { 'format': '%s', 'value': 'rc ? rc : "<nil>"' },
'const char *': { 'format': '%s', 'value': 'rc ? rc : "<nil>"' },
'DIR *': { 'format': '%p', 'value': '(void *) rc' },
'FILE *': { 'format': '%p', 'value': '(void *) rc' },
'FTS *': { 'format': '%p', 'value': '(void *) rc' },
'gid_t': { 'format': '%ld', 'value': ' (long) rc' },
'int': { 'format': '%d', 'value': 'rc' },
'long': { 'format': '%ld', 'value': 'rc' },
'mode_t': { 'format': '0%lo', 'value': '(long) rc' },
'off_t': { 'format': '%lld', 'value': '(long long) rc' },
'size_t': { 'format': '%lu', 'value': '(unsigned long) rc' },
'ssize_t': { 'format': '%ld', 'value': '(long) rc' },
'struct group *': { 'format': '%p', 'value': '(void *) rc' },
'struct passwd *': { 'format': '%p', 'value': '(void *) rc' },
'uid_t': { 'format': '%ld', 'value': ' (long) rc' },
'void *': { 'format': '%p', 'value': 'rc' },
'void': { 'format': 'void%s', 'value': '""' },
}
class Function:
"""A function signature and additional data about how the function works"""
def __init__(self, port, line):
# table of known default values:
default_values = {
'gid_t': '0',
'uid_t': '0',
'int': '-1',
'long': '-1',
'mode_t': '0',
'ssize_t': '-1'
}
self.dirfd = 'AT_FDCWD'
self.flags = '0'
self.port = port
self.directory = ''
self.version = 'NULL'
# On Darwin, some functions are SECRETLY converted to foo$INODE64
# when called. So we have to look those up for real_*
self.inode64 = None
self.real_func = None
self.paths_to_munge = []
self.specific_dirfds = {}
self.hand_wrapped = None
self.async_skip = None
# used for the copyright date when creating stub functions
self.date = datetime.date.today().year
function, comments = line.split(';')
comment = re.search('/\* *(.*) *\*/', comments)
if comment:
self.comments = comment.group(1)
else:
self.comments = None
bits = re.match('([^(]*)\((.*)\)', function)
type_and_name = Argument(bits.group(1))
self.type, self.name = type_and_name.type, type_and_name.name
# convenient to have this declared here so we can use its .decl later
if self.type != 'void':
self.return_code = Argument("%s rc" % self.type)
# Some args get special treatment:
# * If the arg has a name ending in 'path', we will canonicalize it.
# * If the arg is named 'dirfd' or 'flags', it becomes the default
# values for the dirfd and flags arguments when canonicalizing.
# * If the name ends in dirfd, we do the same fancy stuff.
# * Note that the "comments" field (/* ... */ after the decl) can
# override the dirfd/flags values.
self.args = ArgumentList(bits.group(2))
for arg in self.args.args:
# ignore varargs, they never get these special treatments
if arg.vararg:
pass
elif arg.name.endswith('dirfd'):
if len(arg.name) > 5:
self.specific_dirfds[arg.name[:-5]] = True
self.dirfd = 'dirfd'
elif arg.name == 'flags':
self.flags = 'flags'
elif arg.name.endswith('path'):
self.paths_to_munge.append(arg.name)
# pick default values
if self.type == 'void':
self.default_value = ''
elif self.type[-1:] == '*':
self.default_value = 'NULL'
else:
try:
self.default_value = default_values[self.type]
except KeyError:
raise KeyError("Function %s has return type %s,"
"for which there is no default value." %
(self.name, self.type))
# handle special comments, such as flags=AT_SYMLINK_NOFOLLOW
if self.comments:
modifiers = self.comments.split(', ')
for mod in modifiers:
key, value = mod.split('=')
value = value.rstrip()
setattr(self, key, value)
def maybe_inode64(self):
if self.inode64 and os.uname()[0] == 'Darwin':
return "$INODE64"
else:
return ""
def end_maybe_skip(self):
if self.hand_wrapped:
return """/* Hand-written wrapper for this function. */
#endif
"""
else:
return ""
def maybe_skip(self):
if self.hand_wrapped:
return """/* Hand-written wrapper for this function. */
#if 0
"""
else:
return ""
def maybe_async_skip(self):
if self.async_skip:
return """/* This function is not called if pseudo is configured --enable-force-async */
#ifdef PSEUDO_FORCE_ASYNC
if (!pseudo_allow_fsync) {
PROFILE_DONE;
return %s;
}
#endif
""" % self.async_skip
else:
return ""
def comment(self):
"""declare self (in a comment)"""
return self.decl(comment = True)
def decl(self, comment=False, wrap=True):
"""declare self"""
if self.type[-1:] == '*':
spacer = ''
else:
spacer = ' '
return "%s%s%s(%s)" % \
(self.type, spacer, self.name, self.args.decl(comment, wrap))
def decl_args(self):
"""declare argument list"""
return self.args.decl()
def wrap_args(self):
"""declare argument list for wrap_foo() variant"""
return self.args.decl(wrap = True)
def call_args(self):
"""present argument list for a function call"""
return self.args.call()
def fix_paths(self):
"""create/allocate canonical paths"""
fix_paths = []
for path in self.paths_to_munge:
prefix = path[:-4]
if prefix not in self.specific_dirfds:
prefix = ''
fix_paths.append(
"%s = pseudo_root_path(__func__, __LINE__, %s%s, %s, %s);" %
(path, prefix, self.dirfd, path, self.flags))
return "\n\t\t".join(fix_paths)
def real_predecl(self):
if self.real_func:
return self.decl().replace(self.name, self.real_func, 1) + ";"
else:
return ""
def real_init(self):
if self.real_func:
return self.real_func
else:
return "NULL"
def rc_return(self):
"""return rc (or just return)"""
if self.type == 'void':
return "return;"
else:
return "return rc;"
def rc_format(self):
"""the format string to use for the return value"""
return typedata.get(self.type, { 'format': '[%s]', 'value': '"' + self.type + '"' })['format']
def rc_value(self):
"""the value to pass for the format string for the return value"""
return typedata.get(self.type, { 'format': '[%s]', 'value': '"' + self.type + '"' })['value']
def rc_decl(self):
"""declare rc (if needed)"""
if self.type == 'void':
return ""
else:
return "%s = %s;" % (self.return_code.decl(), self.default_value)
def rc_assign(self):
"""assign something to rc (or discard it)"""
if self.type == 'void':
return "(void)"
else:
return "rc ="
def def_return(self):
"""return default value (or just return)"""
if self.type == 'void':
return "return;"
else:
return "return %s;" % self.default_value
def __getitem__(self, key):
"""Make this object look like a dict for Templates to use"""
try:
attr = getattr(self, key)
except AttributeError:
# There's a few attributes that are handled inside the args
# object, so check there too...
attr = getattr(self.args, key)
if callable(attr):
return attr()
else:
return attr
def __repr__(self):
pretty = "%(name)s returns %(type)s and takes " % self
pretty += repr(self.args)
if self.comments:
pretty += ' (%s)' % self.comments
return pretty
def funcdeps(self):
return 'pseudo_wrappers.o: ports/%s/guts/%s.c' % ( self.port, self.name )
class Port:
"""
A Port is a set of function declarations and code providing
details specific to a specific host environment, such as Linux.
Ports can override each other, and each port can indicate
additional ports to include.
"""
def __init__(self, port, sources):
self.name = port
self.subports = []
self.preports = []
print port
if os.path.exists(self.portfile("pseudo_wrappers.c")):
self.wrappers = self.portfile("pseudo_wrappers.c")
else:
self.wrappers = None
if os.path.exists(self.portfile("portdefs.h")):
self.portdef_file = self.portfile("portdefs.h")
else:
self.portdef_file = None
if os.path.exists(self.portfile("wrapfuncs.in")):
self.funcs = process_wrapfuncs(port)
else:
self.funcs = {}
for source in sources:
source.emit('port', self)
if os.path.exists(self.portfile("preports")):
subport_proc = subprocess.Popen([self.portfile("preports"), self.name], stdout=subprocess.PIPE)
portlist = subport_proc.communicate()[0]
retcode = subport_proc.poll()
if retcode:
raise Exception("preports script failed for port %s" % self.name)
for preport in string.split(portlist):
next = Port(preport, sources)
self.preports.append(next)
if os.path.exists(self.portfile("subports")):
subport_proc = subprocess.Popen([self.portfile("subports"), self.name], stdout=subprocess.PIPE)
portlist = subport_proc.communicate()[0]
retcode = subport_proc.poll()
if retcode:
raise Exception("subports script failed for port %s" % self.name)
for subport in string.split(portlist):
next = Port(subport, sources)
self.subports.append(next)
def functions(self):
mergedfuncs = {}
for pre in self.preports:
prefuncs = pre.functions()
for name in prefuncs.keys():
if name in mergedfuncs:
print "Warning: %s from %s overriding %s" % (name, pre.name, mergedfuncs[name].port)
mergedfuncs[name] = prefuncs[name]
for name in self.funcs.keys():
if name in mergedfuncs:
print "Warning: %s from %s overriding %s" % (name, self.name, mergedfuncs[name].port)
mergedfuncs[name] = self.funcs[name]
for sub in self.subports:
subfuncs = sub.functions()
for name in subfuncs.keys():
if name in mergedfuncs:
print "Warning: %s from %s overriding %s" % (name, sub.name, mergedfuncs[name].port)
mergedfuncs[name] = subfuncs[name]
return mergedfuncs
def define(self):
return '#define PSEUDO_PORT_%s 1' % string.upper(self.name).replace('/', '_')
def portdeps(self):
deps = []
if self.wrappers:
deps.append(self.wrappers)
if self.portdef_file:
deps.append(self.portdef_file)
if deps:
return 'pseudo_wrappers.o: %s' % ' '.join(deps)
else:
return '# no extra dependencies for %s.' % self.name
def portdefs(self):
if self.portdef_file:
return '#include "%s"' % self.portdef_file
else:
return '/* no portdefs for %s */' % self.name
def include(self):
if self.wrappers:
return '#include "%s"' % self.wrappers
else:
return '/* no #include for %s */' % self.name
def portfile(self, name):
return "ports/%s/%s" % (self.name, name)
def __getitem__(self, key):
"""Make this object look like a dict for Templates to use"""
try:
attr = getattr(self, key)
except AttributeError:
return None
if callable(attr):
return attr()
else:
return attr
def process_wrapfuncs(port):
"""Process a wrapfuncs.in file, generating a list of prototypes."""
filename = "ports/%s/wrapfuncs.in" % port
funcs = {}
directory = os.path.dirname(filename)
sys.stdout.write("%s: " % filename)
funclist = open(filename)
for line in funclist:
line = line.rstrip()
if line.startswith('#') or not line:
continue
try:
func = Function(port, line)
func.directory = directory
funcs[func.name] = func
sys.stdout.write(".")
except Exception, e:
print "Parsing failed:", e
exit(1)
funclist.close()
print ""
return funcs
def main(argv):
"""Read in function definitions, write out files based on templates."""
funcs = []
sources = []
for arg in argv:
name, value = arg.split('=')
os.environ["port_" + name] = value
# error checking helpfully provided by the exception handler
copyright_file = open('guts/COPYRIGHT')
TemplateFile.copyright = copyright_file.read()
copyright_file.close()
for path in glob.glob('templates/*'):
try:
print "Considering template: " + path
source = TemplateFile(path)
if source.name.endswith('.c') or source.name.endswith('.h'):
source.emit('copyright')
source.emit('header')
sources.append(source)
except IOError:
print "Invalid or malformed template %s. Aborting." % path
exit(1)
try:
port = Port('common', sources)
except KeyError:
print "Unknown uname -s result: '%s'." % uname_s
print "Known system types are:"
print "%-20s %-10s %s" % ("uname -s", "port name", "description")
for key in host_ports:
print "%-20s %-10s %s" % (key, host_ports[key],
host_descrs[host_ports[key]])
# the per-function stuff
print "Writing functions...",
all_funcs = port.functions()
for name in sorted(all_funcs.keys()):
# populate various tables and files with each function
for source in sources:
source.emit('body', all_funcs[name])
print "done. Cleaning up."
for source in sources:
# clean up files
source.emit('footer')
source.close()
if __name__ == '__main__':
main(sys.argv[1:])