# Copyright, 1999, Regents of the University of California
# Please see file Legal.htm
"""Characteristics of Fortran compilers."""
import string, os, types
import semantics, configuration
class Typeinfo:
"A Fortran type translation table."
def __init__ (self, ctype, pcode, parray):
self._ctype = ctype
self._pcode = pcode
self._parray = parray
def python_code (self):
return self._pcode
def python_array_type (self):
return self._parray
def c_type (self):
return self._ctype
def __repr__ (self):
s = str((self.c_type(), self.python_code(), self.python_array_type()))
return 'Typeinfo' + s
t_int = Typeinfo("int", "i", "PyArray_INT")
t_long = Typeinfo("long", "l", "PyArray_LONG")
t_float = Typeinfo("float", "f", "PyArray_FLOAT")
t_double = Typeinfo("double", "d", "PyArray_DOUBLE")
# note that there is no complex float scalar in Python
# code given is a fake and should fail if not coded around by generator
t_complex = Typeinfo("Py_complex_float", "F", "PyArray_CFLOAT")
# note that Py_BuildValue does not recognize D.
t_dcomplex =Typeinfo("Py_complex", "D", "PyArray_CDOUBLE")
t_char =Typeinfo("char", "c", "PyArray_STRING")
t_string = Typeinfo("char*", "s#", "PyArray_STRING")
t_none = Typeinfo("void", "Error", "Error")
standard_typedict = {
"none": t_none,
"integer": t_long,
"real" : t_float,
"real(4)" : t_float,
"real(8)" : t_double,
"doubleprecision" : t_double,
"complex" : t_complex,
"complex(4)" : t_complex,
"complex(8)" : t_dcomplex,
"doublecomplex" : t_dcomplex,
"character" : t_char,
"character*(*)": t_string ,
}
alpha_typedict = { \
"none": t_none,
"integer": t_int,
"real" : t_float,
"real(4)" : t_float,
"real(8)" : t_double,
"doubleprecision" : t_double,
"complex" : t_complex,
"complex(4)" : t_complex,
"complex(8)" : t_dcomplex,
"doublecomplex" : t_dcomplex,
"character" : t_char,
"character*(*)": t_string \
}
class Compiler:
"""Abstract interface to compiler name mangling, etc.
Each realization must define attributes "dirlist" and "liblist" giving the
locations and names of the run-time libraries for passing to linker.
"""
def link_name (self, name):
"Return the linker's name for a Fortran routine named name."
return name
def executable_name (self):
"Return the name you type to execute the Fortran compiler."
raise RuntimeError, "executable_name method missing, contact Pyfort author."
def transpose_option (self):
"Return the default value for TRANSPOSE_OPTION."
return 1
def mirror_option (self):
"Return the default value for MIRROR_OPTION."
return 2
def header(self):
"Return a string containing anything needed to add to the header."
return ''
def c_type (self, dec):
"Return the basic C type for a type."
assert semantics.is_FortranDeclaration(dec)
ftype = dec.type
f = str(ftype)
try:
c = self.typedict[f].c_type()
except KeyError:
print "Unknown Fortran type: " + f
raise SystemExit, 1
return c
def python_code (self, dec):
"Return the PyArg_ParseTuple code for a declaration."
assert semantics.is_FortranDeclaration(dec)
ftype = dec.type
f = str(ftype)
try:
return self.typedict[f].python_code()
except KeyError:
print "Unknown Fortran type: " + f
raise SystemExit, 1
def python_array_type (self, dec):
"Return the PyArray_... type for a declaration."
assert semantics.is_FortranDeclaration(dec)
ftype = dec.type
f = str(ftype)
try:
return self.typedict[f].python_array_type()
except KeyError:
print "Unknown Fortran type: " + f
raise SystemExit, 1
def modify_routine(self, routine):
# first replace any floating-type arguments with length-one arrays
# this will ensure uniform treatment of casting, work around
# problems of no Py_complex_float, etc.
for a in routine.arguments():
d = routine.declaration(a)
ct = self.c_type(d)
if ct in ["float","double","Py_complex_float","Py_complex"]:
if not d.rank():
d.dimlist = ["1"]
def actual_argument_list(self, routine):
"Return the actual argument list to be used in a call to routine."
formal_argument_list = routine.arguments()
aal = []
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if d.rank():
aa = '(' + c + '*) (a' + n + '->data)'
elif c == "char*":
aa = n
else:
aa = '&' + n
aal.append(aa)
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
aal.append('n' + n)
return aal
def parser_argument_list(self, routine):
"Return the argument list portion of the PyArg_ParseTuple call."
python_argument_list = routine.python_inputs()
pal = []
for a in python_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
aa = '&' + n + ', &n' + n
else:
aa = '&' + n
pal.append(aa)
if pal:
return string.join(pal, ", ")
else:
return ''
def build_argument_list(self, routine, flag):
"Return the argument list portion of the Py_BuildValue call."
return_value_list = routine.python_outputs()
bal = []
for a in return_value_list:
d = routine.declaration(a)
c = self.c_type(d)
if routine.is_function() and a == routine.routine_name():
# The is_function() check is needed because a function may have
# an argument a with the same name as the routine.
if flag:
n = "rfortran_result_array"
else:
n = "fortran_result"
elif d.rank():
n = 'r' + string.upper(a)
else:
n = string.upper(a)
if c == "char*":
aa = n + ', n' + string.upper(a)
else:
aa = n
bal.append(aa)
if bal:
return "," + string.join(bal, ", ")
else:
return ''
def prototype(self, routine):
"""Return the prototype for the Fortran call"""
routine_name = routine.routine_name()
link_name = self.link_name (routine_name)
c_return_type = self.c_type(routine.declaration(routine_name))
D = {}
D['link_name'] = link_name
D['return_type'] = c_return_type
prototype_body = ''
prototrail = ''
formal_argument_list = routine.arguments()
for a in formal_argument_list:
d = routine.declaration (a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
pr = c
st = ', int n' + n
else:
pr = c + '*'
st = ''
if prototype_body:
prototype_body = prototype_body + ', '
prototype_body = prototype_body + \
pr + ' ' + string.upper(a)
prototrail = prototrail + st
prototype_total = prototype_body + prototrail
if not prototype_total: prototype_total = 'void'
D['prototype'] = prototype_total
text = \
"""
extern %(return_type)s %(link_name)s(%(prototype)s);
"""
return text % D
class F77Compiler (Compiler):
def __init__ (self, typedict=standard_typedict,
ppu_option = 0,
case_option="lower",
dirlist=[],
liblist=[],
ename= "f77"):
self.typedict = typedict
self.ppu_option = ppu_option
self.case_option = case_option
self.dirlist = dirlist
self.liblist = liblist
self._e = ename
def executable_name (self):
return self._e
def set_ppu (self, ppu):
self.ppu_option = ppu
def set_case (self, to_case):
self.case_option = to_case
def header(self):
"Return a string containing anything needed to add to the header."
text = \
"""
/*
Built by PyFort for compiler with link conventions:
Case option: %s
Post-position underscore: %d
Strings passed as address plus extra int argument for length.
*/
"""
return text % (self.case_option, self.ppu_option)
def link_name (self, name):
if self.case_option == "as-s":
n = name
elif self.case_option == "upper":
n = string.upper(name)
else:
n = string.lower(name)
if self.ppu_option:
n = n + '_'
return n
class AbsoftCompiler (F77Compiler):
def __init__ (self,
dirlist,
case_option="lower",
liblist=['V77','fio','U77','f77math'],
ename='f77'):
F77Compiler.__init__ (self, typedict=standard_typedict,
ppu_option=0,
case_option=case_option,
dirlist=dirlist,
liblist=liblist,
ename=ename)
def modify_routine(self, routine):
"Absoft compiler needs special handling of character arguments"
Compiler.modify_routine (self, routine)
if routine.return_type().name=='character':
routine.make_procedure()
class G77Compiler (Compiler):
def __init__ (self, typedict=standard_typedict,dirs=[]):
self.dirlist = dirs
self.liblist = ['g2c']
self.typedict = typedict
def executable_name (self):
return "g77"
def header(self):
"Return a string containing anything needed to add to the header."
text = \
"""
/*
Built by PyFort for g77 compiler.
*/
"""
return text
def link_name (self, name):
j = string.find(name, "_")
if j < 0:
n = name + '_'
else:
n = name + '__'
return string.lower(n)
class GFortranCompiler (Compiler):
def __init__ (self, typedict=standard_typedict,dirs=[]):
self.dirlist = dirs
self.liblist = []
self.typedict = typedict
def executable_name (self):
return "gfortran"
def header(self):
"Return a string containing anything needed to add to the header."
text = \
"""
/*
Built by PyFort for gfortran compiler.
*/
"""
return text
def link_name (self, name):
j = string.find(name, "_")
if j < 0:
n = name + '_'
else:
n = name + '__'
return string.lower(n)
class Fort77Compiler (Compiler):
def __init__ (self, typedict=standard_typedict,lib='f2c'):
self.dirlist = []
self.liblist = [lib]
self.typedict = typedict
def executable_name (self):
return "fort77"
def header(self):
"Return a string containing anything needed to add to the header."
text = \
"""
/*
Built by PyFort for the fort77 (GNU Darwin) compiler.
*/
"""
return text
def modify_routine(self, routine):
"Fort77 compiler needs special handling of character arguments"
Compiler.modify_routine (self, routine)
intent = "hidden"
type = semantics.FortranType ("integer")
if routine.return_type().name=='character':
routine.make_procedure()
for argument in routine.arguments():
if self.c_type(routine.dict[argument]) == "char":
hidden_argument = 'n' + argument + 'hidden'
routine.add_argument (hidden_argument, type, intent, argument)
def link_name (self, name):
j = string.find(name, "_")
if j < 0:
n = name + '_'
else:
n = name + '__'
return string.lower(n)
def actual_argument_list(self, routine):
"Return the actual argument list to be used in a call to routine."
formal_argument_list = routine.arguments()
aal = []
if routine.return_type().name=='character':
aa = '&fortran_result'
aal.append(aa)
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if d.rank():
aa = '(' + c + '*) (a' + n + '->data)'
aal.append(aa)
elif c == "char*":
aa = n
aal.append(aa)
else:
aa = '&' + n
aal.append(aa)
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
aal.append('n' + n)
return aal
class IfcCompiler (F77Compiler):
def __init__ (self,
dirlist,
liblist,
ename='ifc'):
F77Compiler.__init__ (self, typedict=standard_typedict,
ppu_option=1,
dirlist=dirlist,
liblist=liblist,
ename=ename)
def header(self):
"Return a string containing anything needed to add to the header."
text = \
"""
/*
Built by PyFort for the Ifc (Intel fortran) compiler.
*/
"""
return text
def modify_routine(self, routine):
"Intel fortran compiler needs special handling of character arguments"
Compiler.modify_routine (self, routine)
if routine.return_type().name=='character':
routine.make_procedure()
def actual_argument_list(self, routine):
"Return the actual argument list to be used in a call to routine."
formal_argument_list = routine.arguments()
aal = []
routine_name = routine.head[1]
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if d.rank():
aa = '(' + c + '*) (a' + n + '->data)'
aal.append(aa)
elif c == "char*":
aal.append(n)
else:
aa = '&' + n
aal.append(aa)
# if this is first argument of a function returning character
# whose return value has been transformed into an output argument
if a == routine_name + 'arg':
# then, size of first output argument is passed by value as second argument
aa = 'sizeof(' + n + ')'
aal.append(aa)
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
aal.append('n' + n)
return aal
def prototype(self, routine):
"""Return the prototype for the Fortran call"""
routine_name = routine.routine_name()
link_name = self.link_name (routine_name)
c_return_type = self.c_type(routine.declaration(routine_name))
D = {}
D['link_name'] = link_name
D['return_type'] = c_return_type
prototype_body = ''
prototrail = ''
formal_argument_list = routine.arguments()
routine_name = routine.head[1]
for a in formal_argument_list:
d = routine.declaration (a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
pr = c + ' ' + n
st = ', int n' + n
else:
pr = c + '* ' + n
st = ''
# if this is first argument of a function returning character
# whose return value has been transformed into an output argument
if a == routine_name + 'arg':
# then, size of first output argument (integer) is passed by value as 2nd arg
pr = pr + ', int n' + n
if prototype_body:
prototype_body = prototype_body + ', '
prototype_body = prototype_body + pr
prototrail = prototrail + st
prototype_total = prototype_body + prototrail
if not prototype_total: prototype_total = 'void'
D['prototype'] = prototype_total
text = \
"""
extern %(return_type)s %(link_name)s(%(prototype)s);
"""
return text % D
class CCompiler (Compiler):
def __init__ (self, typedict=standard_typedict):
self.dirlist = []
self.liblist = []
self.typedict = typedict
def executable_name (self):
return "cc" # this is used in generator.py.
def transpose_option (self):
"Return the default value for TRANSPOSE_OPTION."
return 0
def mirror_option (self):
"Return the default value for MIRROR_OPTION."
return 0
def header(self):
"Return a string containing anything needed to add to the header."
text = \
"""
/*
Built by PyFort for C compiler.
*/
"""
return text
def modify_routine(self, routine):
"No modifications are needed for the C compiler"
pass
def actual_argument_list(self, routine):
"Return the actual argument list to be used in a call to routine."
formal_argument_list = routine.arguments()
aal = []
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if d.rank():
if d.rank() > 1 and routine.dict[a].allocatable:
aa = d.rank()*'p' + 'a' + n
else:
aa = '(' + c + '*'*d.rank() + ') (a' + n + '->data)'
elif routine.dict[a].intent=='out':
aa = '&'+n
else:
aa = n
aal.append(aa)
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
aal.append('n' + n)
return aal
def prototype(self, routine):
"""Return the prototype for the C call"""
routine_name = routine.routine_name()
link_name = self.link_name (routine_name)
c_return_type = self.c_type(routine.declaration(routine_name))
c_return_type = c_return_type.replace('*','')
D = {}
D['link_name'] = link_name
D['return_type'] = c_return_type
prototype_body = ''
prototrail = ''
formal_argument_list = routine.arguments()
for a in formal_argument_list:
d = routine.declaration (a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
pr = c
st = ', int n' + n
elif d.rank():
pr = c + '*'*d.rank()
st = ''
elif routine.dict[a].intent=='out':
pr = c + '*'
st = ''
else:
pr = c
st = ''
if prototype_body:
prototype_body = prototype_body + ', '
prototype_body = prototype_body + \
pr + ' ' + string.upper(a)
prototrail = prototrail + st
prototype_total = prototype_body + prototrail
if not prototype_total: prototype_total = 'void'
D['prototype'] = prototype_total
text = \
"""
extern %(return_type)s %(link_name)s(%(prototype)s);
"""
return text % D
class VF77Compiler (F77Compiler):
def actual_argument_list(self, routine):
"Return the actual argument list to be used in a call to routine."
formal_argument_list = routine.arguments()
aal = []
for a in formal_argument_list:
d = routine.declaration(a)
c = self.c_type(d)
n = string.upper(a)
if d.rank():
aa = '(' + c + '*) (a' + n + '->data)'
elif c == "char*":
aa = n
else:
aa = '&' + n
aal.append(aa)
if c == "char*":
aal.append('n' + n)
return aal
def prototype(self, routine):
"""Return the prototype for the Fortran call"""
routine_name = routine.routine_name()
link_name = self.link_name (routine_name)
c_return_type = self.c_type(routine.declaration(routine_name))
D = {}
D['link_name'] = link_name
D['return_type'] = c_return_type
prototype_body = ''
formal_argument_list = routine.arguments()
for a in formal_argument_list:
d = routine.declaration (a)
c = self.c_type(d)
n = string.upper(a)
if c == "char*":
pr = c
st = ', int n' + n
else:
pr = c + '*'
st = ''
if prototype_body:
prototype_body = prototype_body + ', '
prototype_body = prototype_body + \
pr + ' ' + string.upper(a) + st
if not prototype_body: prototype_body = 'void'
D['prototype'] = prototype_body
text = \
"""
extern __declspec(dllimport) %(return_type)s __stdcall %(link_name)s(%(prototype)s);
"""
return text % D
compiler_ids = [
'default',
'solaris',
'gcc',
'cc',
'absoft77',
'absoft90',
'pgf77',
'pgf90',
'g77',
'g77alpha',
'sgi',
'vf',
'f77_OSF1',
'macg77',
'fort77',
'ifc',
'gfortran',
]
def get_compiler (compiler_id):
"Return a compiler object based on compiler_id."
if compiler_id == 'default':
compiler_id = configuration.default_compiler
if compiler_id not in compiler_ids:
print "Compiler named", compiler_id, "is not defined."
raise SystemExit, 1
elif compiler_id == 'solaris':
return F77Compiler(typedict=standard_typedict, ppu_option=1,
dirlist=['/opt/SUNWspro/SC4.2/lib'],
liblist=['F77','M77','sunmath','m','c']
)
elif compiler_id == 'gcc':
return CCompiler()
elif compiler_id == 'cc':
return CCompiler()
elif compiler_id == 'absoft77':
try:
ABSOFT = os.environ.get('ABSOFT')
except KeyError:
print "You must set environment variable ABSOFT to use the ABSOFT compilers."
raise SystemExit, 1
return AbsoftCompiler(dirlist=[ABSOFT+'/lib'],
liblist=['V77','fio','U77','f77math'],
ename='f77')
elif compiler_id == 'absoft90':
try:
ABSOFT = os.environ.get('ABSOFT')
except KeyError:
print "You must set environment variable ABSOFT to use the ABSOFT compilers."
raise SystemExit, 1
return AbsoftCompiler(dirlist=[ABSOFT+'/lib'],
case_option='upper',
liblist=['U77','fio','f77math','f90math'],
ename = "f90")
elif compiler_id == 'pgf77':
try:
PGI = os.environ.get('PGI')
except KeyError:
print "You must set environment variable PGI to use the PG compilers."
raise SystemExit, 1
PGI_VERSION = os.environ.get('PGI_VERSION','')
if not os.path.exists(PGI+'/linux86/'+PGI_VERSION+'/lib'):
if not os.path.exists(PGI+'/linux86/5.1/lib'):
print "You must set environment variable PGI_VERSION correctly to use the PG compilers version 5 and above."
raise SystemExit, 1
else:
PGI_VERSION='5.1'
return F77Compiler(typedict=standard_typedict, ppu_option=1,
dirlist=[PGI + '/linux86/'+PGI_VERSION+'/lib'],
liblist=['pgftnrtl','pgc'],
ename = 'pgf77'
)
elif compiler_id == 'pgf90':
try:
PGI = os.environ.get('PGI')
except KeyError:
print "You must set environment variable PGI to use the PG compilers."
raise SystemExit, 1
PGI_VERSION = os.environ.get('PGI_VERSION','')
if not os.path.exists(PGI+'/linux86/'+PGI_VERSION+'/lib'):
if not os.path.exists(PGI+'/linux86/5.1/lib'):
print "You must set environment variable PGI_VERSION correctly to use the PG compilers version 5 and above."
raise SystemExit, 1
else:
PGI_VERSION='5.1'
return F77Compiler(typedict=standard_typedict, ppu_option=1,
dirlist = [PGI + '/linux86/'+PGI_VERSION+'/lib'],
liblist = ['pgf90',
'pgf90_rpm1',
'pgf902',
'pgf90rtl',
'pghpf',
'pgc'],
ename = 'pgf90'
)
elif compiler_id == 'g77':
return G77Compiler()
elif compiler_id == 'g77alpha':
return G77Compiler(typedict = alpha_typedict)
elif compiler_id == 'sgi':
return F77Compiler(typedict=standard_typedict, ppu_option=1,
dirlist = ['/usr/lib32'],
liblist = ['ftn'],
ename = "f77")
elif compiler_id == 'vf': #win32, Visual Fortran
from configuration import vfroot
return VF77Compiler(typedict=standard_typedict, ppu_option=0,
case_option="upper",
dirlist=[os.path.join(vfroot, 'Lib')],
liblist =['dfor'],
ename= os.path.join(vfroot, 'Bin', 'df.exe')
)
elif compiler_id == 'f77_OSF1': #Dec alpha
return F77Compiler(typedict=alpha_typedict, ppu_option=1,
dirlist=['/usr/ccs/lib/cmplrs/fortrtl'],
liblist=['for','Ufor','Futil'],
ename = 'f77'
)
elif compiler_id == 'fort77': # GNU Darwin fortran compiler for Macintosh
return Fort77Compiler()
elif compiler_id == 'gfortran': # GNU Darwin fortran compiler for Macintosh
return GFortranCompiler()
elif compiler_id == 'macg77': # GNU fortran compiler g77 on the Macintosh
return G77Compiler(dirs=['/sw/lib'])
elif compiler_id == 'ifc': # Intel fortran compiler for IA32 platform
return IfcCompiler(dirlist=['/opt/intel/compiler70/ia32/lib'],
liblist=['irc','cxa','cprts','unwind',
'imf','F90','intrins','IEPCF90','CEPCF90','m','c'],
ename = 'ifc'
)
|