# 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): self.dirlist = [] 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 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 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', 'f77', 'g77alpha', 'sgi', 'vf', 'f77_OSF1', 'macg77', 'fort77' ] 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 return F77Compiler(typedict=standard_typedict, ppu_option=1, dirlist=[PGI + '/linux86/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 return F77Compiler(typedict=standard_typedict, ppu_option=1, dirlist = [PGI + '/linux86/lib'], liblist = ['pgf90', 'pgf90_rpm1', 'pgf902', 'pgf90rtl', 'pghpf', 'pgc'], ename = 'pgf90' ) elif compiler_id == 'g77': return G77Compiler() elif compiler_id == 'f77': return F77Compiler() 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 == 'macg77': # GNU fortran compiler g77 on the Macintosh return Fort77Compiler(lib='g2c')