import sys, os, glob, string, types
import grammar, generator, fortran_compiler, configuration, version
from distutils.core import setup,Extension
default_makefile = """\
SOURCES=%(sources)s
lib%(name)s.a: $(SOURCES)
\t$(Pyfort_COMPILER) $(Pyfort_COMPILER_OPTIONS) -c $(SOURCES)
\tld -r -o lib%(name)s.a *.o
clean:
\t/bin/rm -f lib%(name)s.a *.o
"""
def execute (line):
print line
result = os.system(line)
if result:
raise ValueError, 'Error executing '+line
class pyf:
"""pyf(filename,
sources=[],
libraries = '',
library_directories = '',
compiler_options = '',
python_directory = '',
generate_as = '',
package_name = '',
freeform = 0,
use_c_compiler = 0,
)
Represents a Pyfort input file that depends on the given sources.
The sources can be given as a list and will be used as input to
glob.glob.
libraries is a list of library objects.
python_directory = directory holding associated python modules, if any.
freeform = 1 or 0, indicating whether format of file is freeform or
column 1 has special meaning
package_name is an optional name for the module into which the
Python code if any and the generated Pyfort extension should be
installed.
"""
def __init__ (self, filename, **kw):
if filename:
head, tail = os.path.split(filename)
self.name, junk = os.path.splitext (tail)
else:
self.name = ''
self.filename = filename
self.sources = kw.get('sources', [])
self.libraries = kw.get('libraries', '')
self.library_directories = kw.get('library_directories', '')
self.compiler_options = kw.get('compiler_options', '')
self.generate_as = kw.get('generate_as', '')
self.use_c_compiler = kw.get('use_c_compiler',0)
self.freeform = kw.get('freeform', 0)
self.python_directory = kw.get('python_directory', '')
self.package_name = kw.get('package_name', '')
def __repr__ (self):
result = "pyf('%s'" % self.filename
if self.sources:
result = result + ", \n sources=" + repr(self.sources)
if self.libraries:
result = result + ", \n libraries=" + repr(self.libraries)
if self.library_directories:
result = result + ", \n library_directories=" + repr(self.library_directories)
if self.compiler_options:
result = result + ", \n compiler_options=" + repr(self.compiler_options)
if self.python_directory != '':
result = result + ", \n python_directory=" + \
repr(self.python_directory)
if self.package_name != '':
result = result + ", \n package_name=" + \
repr(self.package_name)
if self.generate_as != '':
result = result + ", \n generate_as=" + \
repr(self.generate_as)
if self.use_c_compiler != 0:
result = result + ", \n use_c_compiler="+repr(self.use_c_compiler)
if self.freeform != 0:
result = result + ", \n freeform=" + repr(self.freeform)
result = result + ")"
return result
def is_advanced (self):
if self.libraries or self.library_directories or \
self.compiler_options or self.python_directory or \
self.package_name:
return 1
else:
return 0
def generated_module_name (self):
if self.generate_as: return self.generate_as
return self.name
def validate (self):
"Check the pyf. If not valid, return an error message."
if not os.path.isfile(self.filename):
return self.filename + "does not exist."
if self.python_directory:
if not os.path.isdir(self.python_directory):
return "In %s, Python directory %s specified does not exist."\
% (self.filename, self.python_directory,)
if os.path.isfile(os.path.join(self.python_directory, '__init__.py')):
# Package case
if not self.package_name:
return """In re %s, Python directory %s
contains an __init__.py file, so you must specify a module name.
""" % (self.filename, self.python_directory)
else: # do not have Python
if self.package_name:
return """%s specifies a package name without a Python directory""" % (self.filename,)
for s in self.sources:
g = glob.glob(s)
if not g:
return self.name + ' source ' + s + \
' does not match any existing file.'
for x in g:
name, ext = os.path.splitext(x)
if not ext:
return self.name + ' source ' + s + \
' lacks an extension. Cannot tell if C or Fortran.'
if ext.lower() == ".c" and not self.use_c_compiler:
return self.name + ' source ' + s + \
' is C, but C option not selected.'
if ext != ".c" and self.use_c_compiler:
return self.name + ' source ' + s + \
' is not C, but C option is selected.'
return ''
def expanded_sources (self):
"""The expanded list of source files."""
sources = []
for x in self.sources:
sources += [os.path.abspath(\
os.path.expanduser(y)) \
for y in glob.glob(x)
]
return sources
def build (self, flags):
""" build(flags); flags is a dictionary set up by the outer build.
There are three cases:
1. Python package, install everything in it.
2. Python supplied but it isn't a package
-- install it and the C at top level
3. No python -- just install C extension at top level
For cleanliness and uninstall, put everything in an "extra_path" area.
"""
print "Building Pyfort module", self.name
# This validation removes such coding from build itself.
msg = self.validate()
if msg:
raise SystemExit, msg
library_directory_list = self.library_directories.split()
library_names = self.libraries.split()
libname = 'pyfort_'+self.name
dir = os.path.abspath(os.path.join('build',libname))
outdir = flags.get('outdir','')
if not outdir:
outdir = dir
if not os.path.isdir(outdir):
os.makedirs(dir)
minusg = flags.get('minusg', '')
if self.sources:
library_names.insert(0, libname)
if dir not in library_directory_list:
library_directory_list.append(dir)
if not os.path.isdir(dir):
os.makedirs(dir)
makefile = os.path.join(dir, "Makefile")
if os.path.isfile(makefile):
os.remove(makefile)
d = { 'name': libname,
'sources': string.join(self.expanded_sources()),
}
print >> sys.stderr, "Creating a simple Makefile at ", \
os.path.join(dir, makefile)
f = open(makefile, 'w')
print >> f, default_makefile % d
f.close()
here = os.getcwd()
os.chdir(dir)
if self.use_c_compiler:
if os.environ.has_key('CC'):
c = os.environ['CC']
else:
c = 'cc'
os.environ['Pyfort_COMPILER'] = c
else:
compiler_id = flags.get('fortran_compiler_id', 'default')
compiler_object = fortran_compiler.get_compiler(compiler_id)
compiler_name = compiler_object.executable_name()
os.environ['Pyfort_COMPILER'] = compiler_name
os.environ['Pyfort_COMPILER_OPTIONS'] = self.compiler_options + " " + \
minusg
try:
execute('make')
except ValueError, msg:
print '****** Error occurred; your extension was not built.'
raise SystemExit, 1
os.chdir(here)
command = flags.get('command')
if configuration.prefix:
command = command.replace('install',
'install --prefix='+configuration.prefix)
project_name = flags.get('project_name')+configuration.project_suffix
fortran_compiler_id = flags.get('fortran_compiler_id')
if self.use_c_compiler:
compiler = fortran_compiler.get_compiler('cc')
else:
compiler = fortran_compiler.get_compiler(fortran_compiler_id)
lversion = flags.get('version', version.version)
sys.argv = ["PYFORT"]+command.split() # Distutils uses sys.argv, sick.
gmn = self.generated_module_name()
setupargs = {'name': gmn + "_extension",
'version': lversion,
}
if not configuration.prefix:
setupargs['extra_path'] = project_name
if self.python_directory:
if os.path.isfile(os.path.join(self.python_directory, '__init__.py')):
# Package case
module_id = self.package_name + "." + gmn
setupargs['packages'] = [self.package_name]
setupargs['package_dir'] = {self.package_name:self.python_directory}
else:
# Have Python but not a package
module_id = gmn
setupargs['packages'] = ['']
setupargs['package_dir'] = {'':self.python_directory}
else:
# No Python at all
module_id = gmn
dfile, cfile = process(gmn, self.filename, compiler,
outdir, not self.freeform)
print "Your extension has been generated in %s." %outdir
if command:
if cfile:
# Note we add in the Fortran runtime libraries here...
extspec = {
'name': module_id,
'library_dirs': library_directory_list + compiler.dirlist,
'libraries': library_names + compiler.liblist,
'sources': [cfile],
}
eobj = apply(Extension, [], extspec)
setupargs['ext_modules'] = [eobj]
print "Executing setup ", command, "using these arguments:"
for key, value in setupargs.items():
if key == 'ext_modules':
print " ", "External module specified by:"
for k, v in extspec.items():
print " ", k, "=", repr(v)
else:
print " ", key, "=", repr(value)
print "---------------------------------"
if command != "echo":
apply (setup, [], setupargs)
def process(pyf_module_name, pathname, compiler, outdir, column1_comments):
"""Processes the input file pointed to by pathname to create desired module.
Returns the names of the two files created.
"""
tokens = grammar.scan(pathname, column1_comments)
if not tokens:
return '',''
else:
proclist = grammar.parse(tokens)
print "Generating documentation file"
d = generator.Document_Generator(pyf_module_name, ".", proclist, [])
d.generate()
print "Generating Pyfort interface in ", outdir
w = generator.C_Module_Generator(compiler, pyf_module_name, outdir, proclist, [])
w.generate()
return (d.filename, w.filename)
class project:
"A contributed project"
def __init__ (self, filename):
self.filename = filename
dir, basename = os.path.split(filename)
name, extension = os.path.splitext(basename)
if not name:
raise ValueError, 'Must supply a non-blank name for the project.'
if extension != ".pfp":
raise ValueError, 'Not a project file: '+ filename
self.name = name
self.pyfs = []
print "Reading", self.filename
if os.path.isfile(self.filename):
self.read (self.filename)
else:
print "Note: ", self.filename, "does not exist."
def read (self, filename):
"Read a saved state from the file."
d = {'pyf': self.add_pyf}
execfile(filename, d)
def save (self, filename=None):
if filename is None:
filename = self.filename
f = open(filename, 'w')
print >>f, str(self)
f.close()
def validate (self):
"return a message if project not valid"
for pyf in self.pyfs:
msg = pyf.validate()
if msg: return msg
return ''
def __str__ (self):
result = ""
for item in self.pyfs:
result = result + repr(item) + '\n'
return result
def add_pyf (self, filename, **kw):
self.pyfs.append(pyf(filename, **kw))
def __repr__ (self):
return "project(%s)" % self.filename
def build (self, flags):
flags['package'] = self.name
for item in self.pyfs:
item.build(flags)
def build(filename, flags):
print "Building project", filename
dir, base = os.path.split(filename)
here = os.getcwd()
if dir and dir != here:
print "Changing to directory", dir
os.chdir(dir)
for k in flags: print k, "=", flags[k]
p = project(filename)
p.build(flags)
os.chdir(here)
|