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 (not self.sources) and (not self.libraries): return "In %s, no Python directory, libraries, or sources given."\ % (self.filename,) 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) 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)