Logo Search packages:      
Sourcecode: abinit version File versions  Download package

abilint.py

#!/usr/bin/env python
# -*- coding: us-ascii -*-
#----------------------------------------------------------------------------
# Copyright (C) 2004-2007 ABINIT group (TD)
# This file is distributed under the terms of the
# GNU General Public License, see ~abinit/COPYING
# or http://www.gnu.org/copyleft/gpl.txt .
# For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
#----------------------------------------------------------------------------
# Add a module for each routine in ABINIT
# in each xxx which contains an interface
# for each subroutine inside a directory.
# Modification for the version 5.x of ABINIT
# Date: 07/02/2007
#----------------------------------------------------------------------------

#Principles
#----------
# 1 - Analyze without changing any part of the code
# 2 - Finally change if it is needed

#Plan
#----
# 1 - List all routines and store all in memory.
# 2 - Search all routines belonging to a directory.
# 3 - Remove all interfaces contained in the source files.
# 4 - Create a module containing the interface of all routines
#     for each directory.
# 5 - Add this file in the directory "src/defs".
#     (this is a choice which is not the best I think)
# 6 - Beautify the code if required
# 7 - Build some graphs concerning the calls of routines
# 8 - Copy the new version

#TODO
#----
# Add robodoc header and footer automatically (beautify option)
# Add correct children to _xxx_ files.
# Add children and parents to the files interfaces_xxx.F90.
# Call after a ";" is not detected.
# Handle better the preprocessing directives
# Remove all specific stuff related to abinit for the analyze
# Add warning if generic routines not found

#Description
#----------
#The script abilint uses class to implement the notion of project, files, routines and so on.
#The class Project (maybe Package is more appropriate) contains all the information about directories, files and routines.
#There are lists of:
# - directories (abilint.dirs) which are only names of directories, 
# - files (abilint.files) which are the objects File (see File class) and 
# - routines (abilint.routines) which are the objects Routine (see Routine class). 
#So it is easy to access directly to an object in the project. 
#The only restriction is that the names for the routines and the files have to be unique.
#The class Structure is a general class which implements the method of read or write which is propagated by inheritance
#for File, File_F90, File_F77, Module, Routine, Function, Program, Code, Comment, Declaration, Body, ...
#
#Hierarchy of classes
#--------------------
#File_F77 is a special class of File_F90 with a filter to parse the code
#File_F90 -> Comment (Robodoc_Header, Robodoc_Footer)
#         -> Module   -----> Comment, Declaration, Routine, Function
#         -> Routine  --|
#         -> Function   |
#                        --> Comment, Declaration, Body


#Usage (and help)
def usage(error=0):
    sys.stderr.write("""
abilint [options] <source> <dest>
    where:
      <source> is the directory that contains the ABINIT project 
      <dest>   is the directory of destination
    options:
      --beautify  beautify the code (experimental)
      --graph=<routine1,routine2,...> or --graph=all (experimental)
                  build the graph of calls of the <routine> in the file routine.ps
      --graph=directories
                  build the graph of interdependences between directories
                  (need the package graphviz)
      --help      display this message
      --libraries build the files lib/xxx/_xxx_
      --lint      complete analysis (experimental)
      --nofatal   no fatal message: always generate interfaces
      --only=src/<dir> or --only=lib/<dir>
                  build the files src/defs/interfaces_<dir>.F90
                  only for the directory <dir>
      --verbose   display more information
""")
    sys.exit(error)

#Operating System (used for os.path)
import os
#stderr and stdout
import sys
#Options
import getopt
#Regular expressions
import re
#To dump python object in a file
import cPickle


#Check the version of python
version = map(int,sys.version_info[0:3])
if  version < [2,3,0]:
    sys.stderr.write("Detected version %d.%d.%d\n" % tuple(version))
    sys.stderr.write("Minimal required version is python 2.3.0")
    sys.exit(1)

#If the class 'set' does not exist use instead Set
if 'set' not in dir(__builtins__):
    from sets import Set as set

#Indentation (one blank character)
indent = 1*" "

#Prefix for the modules containing the interfaces.
prefix = "interfaces_"

#Files to generate generic routines
generic_routines = [ "chgbas.F90",
                     "hdr_io.F90",
                     "hdr_io_netcdf.F90",
                     "hdr_skip.F90",
                     "xcast_mpi.F90",
                     "xsum_mpi.F90",
                     "xsum_master.F90",
                     "xmin_mpi.F90",
                     "xmax_mpi.F90",
                     "xexch_mpi.F90",
                     "xallgather_mpi.F90",
                     "xallgatherv_mpi.F90",
                     "xalltoallv_mpi.F90",
                     "xderiveread.F90",
                     "xderivereadval.F90",
                     "xderivewrite.F90",
                     "xderivewriteval.F90",
                     "xexch_mpi.F90" ]

#Routines (in lower case) using optional arguments or generic routines
#If we do not use HAVE_FORTRAN_INTERFACES, we have to specify where the interfaces are
interfaces_routines = { "drivexc":              "defs_xc",
                        "rhohxc":               "defs_xc",
                        "rhohxc_coll":          "defs_xc",
                        "xcxalp":               "defs_xc",
                        "xctetr":               "defs_xc",
                        "xcpbe":                "defs_xc",
                        "xcspol":               "defs_xc",
                        "xcpzca":               "defs_xc",
                        "xcwign":               "defs_xc",
                        "xchelu":               "defs_xc",
                        "xcxalp":               "defs_xc",
                        "wrscr":                "defs_interfaces",
                        "create_netcdf_file":   "defs_interfaces",
                        "write_netcdf_data":    "defs_interfaces",
                        "hdr_io":               "defs_interfaces",
                        "hdr_io_netcdf":        "defs_interfaces",
                        "hdr_skip":             "defs_interfaces",
                        "initberry":            "defs_interfaces",
                        "fftw":                 "defs_interfaces",
                        "calc_b_matrix":        "defs_interfaces",
                        "calc_btinv_matrix":    "defs_interfaces",
                        "calc_prim_int":        "defs_interfaces",
                        "deloc2xcart":          "defs_interfaces",
                        "fred2fdeloc":          "defs_interfaces",
                        "make_angles":          "defs_interfaces",
                        "make_bonds":           "defs_interfaces",
                        "make_dihedral":        "defs_interfaces",
                        "make_prim_internals":  "defs_interfaces",
                        "xcart2deloc":          "defs_interfaces",
                        "xcart2deloc_fixb":     "defs_interfaces",
                        "acfd_dyson":           "defs_dyson",
                        "dyson_ls":             "defs_dyson",
                        "dyson_de":             "defs_dyson",
                        "dyson_gl":             "defs_dyson",
                        "initberry":            "defs_berry",
                        "xallgather_mpi":       "defs_xfuncmpi",
                        "xallgatherv_mpi":      "defs_xfuncmpi",
                        "xalltoallv_mpi":       "defs_xfuncmpi",
                        "xderiveread":          "defs_xderive",
                        "xderivereadval":       "defs_xderive",
                        "xderivewrite":         "defs_xderive",
                        "xderivewriteval":      "defs_xderive",
                        "xcast_mpi":            "defs_xfuncmpi",
                        "xmax_mpi":             "defs_xfuncmpi",
                        "xmin_mpi":             "defs_xfuncmpi",
                        "xsum_master":          "defs_xfuncmpi",
                        "xsum_mpi":             "defs_xfuncmpi",
                        "xexch_mpi":            "defs_xexchmpi" }


#To add before and after statements added by abilint
abilint_start = "!This section has been created automatically by the script Abilint (TD)."\
                + " Do not modify these by hand.\n"
abilint_end =  "!End of the abilint section\n"

use_before = abilint_start + "#ifdef HAVE_FORTRAN_INTERFACES\n"
notuse_before = abilint_start + "#ifndef HAVE_FORTRAN_INTERFACES\n"

use_after = "#endif\n" + abilint_end
notuse_after = use_after

#End of local variables (for abirules)
re_end_variables = re.compile("[\n]*![*]{6,}.*\n",re.MULTILINE)
end_variables = "\n!"+72*"*"+"\n"

#Intrinsic functions of the Fortran
intrinsic_routines = [ "cpu_time",
                        "date_and_time",
                        "system_clock" ]

#Comment "!Arguments"
comment_arguments = "!Arguments " + "-"*50 + "\n"
#Comment "!Local variables"
comment_local_variables = "!Local variables " + "-"*44 + "\n"
#Last line of declaration
comment_declaration = "!" + "*"*60

#Regular expression to detect this section
re_section_abilint = re.compile("[ \n]*" + abilint_start[:11] + ".+?" \
                        + abilint_end.rstrip() + "[^\n]*[\n]*", \
                        re.MULTILINE+re.IGNORECASE+re.DOTALL)
re_section_abilint_start = re.compile("[ \n]*" + abilint_start[:11] + ".+?" \
                        + "#if" + "[^\n]*[\n]*", \
                        re.MULTILINE+re.IGNORECASE+re.DOTALL)
re_section_abilint_end = re.compile("[ \n]*" + "#endif" + ".+?" \
                        + abilint_end.rstrip() + "[^\n]*[\n]*", \
                        re.MULTILINE+re.IGNORECASE+re.DOTALL)

#Regular Expressions
#-------------------
#Detect !Arguments
re_arguments = re.compile("[\n ]*!Argument[^\n]+\n")
#To remove blank lines and comments
re_blank = re.compile('^(([ ]*?)|([ ]*[!].*?))\n', \
                       re.MULTILINE)
re_blank_line = re.compile("^[ \t]*\n")

#Detect call (not after an if or ;)
re_call = re.compile('(^[ \t]+call[ ]*)(\w+)', \
                       re.MULTILINE+re.IGNORECASE)
re_comment_interface = re.compile('^!Interfaces.*?\n', \
                       re.MULTILINE)
#Comments and also preprocessing commands
re_comment_match = re.compile("^([ \t]*(([!#].*)|[ ]*)\n)+")
#Comments and also preprocessing commands
re_comment = re.compile("([ \t]*(([!#].*)|[ ]*)\n)+")
#Contains statement
re_contains = re.compile('^[ ]*contains',re.IGNORECASE)
#Continuation
re_continuation = re.compile("&[ \t]*(![^\n]*)?\n")
#Empty preprocessing directives
re_empty_preproc = re.compile(\
                "^[ ]*#[ \t]*if[^\n]+\n([ ]*#[ \t]*else[ ]*\n)?[ ]*#[ \t]*endif[^\n]*\n?",re.MULTILINE)
#Detect the beginning of a block
re_sub_start = re.compile('[ \t]*(module|program|subroutine|(([^!\'"\n]*?)function))',re.IGNORECASE)
re_sub_end   = re.compile('[ \t]*end[ \t]*(function|module|program|subroutine|\n)',re.IGNORECASE)
re_implicit_none = re.compile('^[ ]*implicit[ ]*none', \
                       re.MULTILINE+re.IGNORECASE)
re_indent = re.compile('^[ \t]*(?!#)',re.MULTILINE)
re_indent_line = re.compile('^[ \t]*')
#Detect !Local
re_local_variables = re.compile("[\n ]*!Local[^\n]+\n")

#Detect module
re_module = re.compile("^[ ]*module[ ]*(?P<name>\w+)", \
                       re.MULTILINE+re.IGNORECASE)
#Use to determine the variable in result statement for a function
re_result = re.compile('result[(](?P<result>[^)]+)[)]')
#Remove not useful use for the interfaces
re_use_unwanted = re.compile('^[ ]+use[ ]*(defs_aimprom|defs_interfaces|defs_xc|defs_generic|defs_xderive|flib_sax|f90_unix|funcofrho_tfw|func_rscgres|netcdf|nqxc|smoothvlocal|defs_xfuncmpi)[ ]*?\n',re.MULTILINE+re.IGNORECASE)
#Remove use replaced by the system of interfaces
re_use_replaced = re.compile('^[ ]+use[ ]*(defs_berry|defs_dyson|defs_interfaces|defs_xc|defs_xderive|defs_xfuncmpi)[ ]*?\n',re.MULTILINE+re.IGNORECASE)


#Function to split variable in declaration statements
def split_variables(string):
    "split declaration into variables: [ (varname,declaration), ...]"
    a = list()
    varname = ""
    declaration = ""
    it = iter(string)
    for c in it:
        if c == "=":
            #Parameters: iterate up to the end
            declaration += c
            for c in it:
                declaration += c
            continue
        #Inside a declaration
        if c == "(":
            par = 1
            while par:
                declaration += c
                c = it.next()
                if c == "(":
                    par += 1
                elif c == ")":
                    par -= 1
            declaration += c
            continue
        if c == " ":
            #Remove
            c = ""
        if c == ",":
            #Remove "," and new variable
            a.append( (varname.lower(),varname+declaration) )
            varname = ""
            declaration = ""
            c = ""
        varname += c
    a.append( (varname.lower(),varname+declaration) )
    return a


#Functions to split the code
def split_code(text):
    "Split a statement of code"
    statements = list()
    statement = list()
    word = ""
    iter_text = iter(text)
    for char in iter_text:
        #Alphanumeric
        if char.isalnum() or char == '_':
            word += char
        else:
            if word:
                statement.append(word)
                word = ''
            if char == ';' or char == '\n':
                #End of statement
                statements.append(statement)
                statement = list()
            elif char == '!':
                #Remove comments
                while char != "\n":
                    char = iter_text.next()
                statements.append(statement)
                statement = list()
            elif char == ' ' or char == '\t':
                #Remove
                pass
            else:
                statement.append(char)
    print statements
    sys.exit(1)
    return statement



#Class for a project (i.e. ABINIT)
class Project:
    "Class to define a project which is a set of directories and files."
    #To avoid .arch-ids directories
    re_arch = re.compile("[.]arch-ids")
    def __init__(self,dir,pattern_dir='.*',pattern_file='.*',name="",logfile="project.log"):
        "Initialisation"
        self.ROOT = dir
        self.name = name
        #Instantiation of the class message
        self.message = Message(verbose=verbose,logfile=logfile)
        #List of directories
        self.dirs = list()
        #List of files in each directory
        self.dirsfiles = dict()
        self.dirsfiles[dir] = list()
        #Dictionary of files referenced by file (the name should be unique)
        self.files = dict()
        #Dictionary of routines (the name should be unique)
        self.routines = dict()
        #Dictionary of modules
        self.modules = dict()
        self.dir_src = "%s/src" % self.ROOT
        #Add directories and files
        self.add(self.dir_src,pattern_dir,pattern_file)
        #List of functions
        self.functions = list()
        #Number of copied files
        self.copied = 0
        #Data which are saved for a new process
        self.cached = dict()
        self.message.write("Project %s (%d directories, %d files)\n" % \
                         (self.name,len(self.dirs),len(self.files)),verbose=-10)
    #
    def add(self,dir,pattern_dir='.*',pattern_file='.*',read_only=False,File_Class=None):
        "Add directories and files with a pattern"
        re_dir = re.compile(pattern_dir)
        re_file = re.compile(pattern_file)
        try:
            files = os.listdir(dir)
        except OSError:
            self.message.fatal("The directory '%s' does not exist!\n" % dir)
        for file in files:
            dd = "%s/%s" % (dir,file)
            if os.path.isdir(dd):
                if self.re_arch.match(file):
                   #we skip it
                   continue
                elif re_dir.match(file):
                    self.message.write("[add dir %s -----> " % dd)
                    if not self.dirsfiles.has_key(dd):
                        self.dirs.append(dd)
                        self.dirsfiles[dd] = list()
                    #Add all files in this sub-directory
                    self.add(dd,pattern_dir,pattern_file,read_only,File_Class)
                    self.message.write(" <----- add dir %s]\n" % dd)
                else:
                    #We ignore it.
                    continue
            elif os.path.isfile(dd):
                if re_file.match(file):
                    self.add_file(dir,file,read_only=read_only,File_Class=File_Class)
    #
    def add_file(self,dir,file,create=False,read_only=False,File_Class=None):
        "Add a file in the project."
        if not self.dirsfiles.has_key(dir):
            self.dirs.append(dir)
            self.dirsfiles[dir] = list()
        if not file in self.dirsfiles[dir]:
            self.dirsfiles[dir].append(file)
        else:
            #Already created: Do nothing
            return
        tt = file.split(".")
        if len(tt) > 1:
            suffix = tt[-1]
        else:
            suffix = ""
        self.message.write("(%s)" % file)
        if File_Class:
            #Use the file type
            self.files[file] = File_Class(file,dir=dir,read_only=read_only,message=self.message)
        elif suffix == "F90":
            if file in generic_routines:
                #Generic routine build for this file.
                self.files[file] = File_F90_Generic(file,dir=dir,\
                        read_only=read_only,message=self.message)
            else:
                self.files[file] = File_F90(file,dir=dir,read_only=read_only,\
                        message=self.message)
        else:
            self.files[file] = File(file,dir=dir,read_only=read_only,\
                    message=self.message)
        if not create:
            self.files[file].read()
    #
    def add_routine(self,routine):
        "Add a routine to the project (routine.lower have to be defined)"
        if routine.lower in self.routines:
            old = self.routines[routine.lower]
            if routine.parent.read_only:
                self.message.error("Two subroutines have the same name:\n" \
                    + "%s in %s/%s\n" % (old.name,old.parent.dir,old.parent.name) \
                    + "%s in %s/%s\n" % (routine.name,routine.parent.dir,routine.parent.name) \
                    + "Read only file so keep the first one.\n")
            else:
                self.message.fatal("Two subroutines have the same name:\n" \
                    + "%s in %s/%s\n" % (old.name,old.parent.dir,old.parent.name) \
                    + "%s in %s/%s\n" % (routine.name,routine.parent.dir,routine.parent.name))
            #Change the name of the routine (to be improved)
            name = routine.lower
            i = 1
            while "%s_%d" % (name,i) in self.routines:
                i += 1
            self.routines["%s_%d" % (name,i)] = routine
        else:
            self.routines[routine.lower] = routine
    #
    def add_use_interface(self):
        "Add 'use interface_' at each file"
        self.message.section("Add 'use interface_xxx' in each routine...")
        #Do for each routine stored in self.routines
        for (name,routine) in self.routines.items():
            self.message.write("(%s/%s <%s>)\n" % (routine.dir,routine.file,name))
            routine.add_use_interface(self)
        self.message.done()
    #
    def analyze_all(self,except_pattern="a"*40):
        "Analyze all the project except the files which contain except_pattern"
        self.message.section("Analyze all the project...")
        for file in self.files.values():
            if except_pattern not in file.name:
                file.analyze(self)
        self.message.done()
        #Build the list of arguments of all routines
        self.get_arguments()
        #Determine the list of functions
        self.set_functions()
    #
    def analyze_body(self):
        "Analyze the body of all routines"
        self.message.section("Analyze the bodies of routines...")
        for (name,routine) in self.routines.items():
            self.message.write("(%s)" % name)
            routine.analyze_body(self)
        self.message.done()
    #
    def analyze_comments(self,except_pattern="a"*40):
        "Analyze the comments of all the files except the files which contain except_pattern"
        self.message.section("Analyze the comments...")
        for file in self.files.values():
            if isinstance(file,File_F90) and except_pattern not in file.name:
                file.analyze_comments(self)
        self.message.done()
    #
    def analyze_directories(self):
        "Analyze the dependencies between directories"
        for routine in self.routines.values():
            dir = routine.dir.split('/')[-1]
            for called in routine.called:
                try:
                    called_dir = self.routines[called].dir.split('/')[-1]
                    num = called_dir[0:2]
                    if num == "li":
                        num = called_dir[3:5]
                    if dir[0:2] < num:
                        self.message.warning("Misplacing: <%s> (%s) calls <%s> (%s)" \
                                % (routine.name,dir,called,called_dir),verbose=-10)
                except KeyError:
                    pass
    #
    def get_arguments(self):
        "Build the list of arguments of all routines"
        self.message.section("Build the list of arguments in each routine...")
        #Do for each routine stored in self.routines
        for (name,routine) in self.routines.items():
            self.message.write("(%s/%s <%s>)\n" % (routine.dir,routine.file,name))
            routine.get_arguments(self)
        self.message.done()
    #
    def backup(self):
        "Backup the code"
        self.message.section("Backup all the code before changing it...")
        for file in self.files.values():
            file.backup()
        self.message.done()
    #
    def beautify(self):
        "Improve the appearance of the code"
        self.message.section("Beautify the code...\n")
        for file in self.files.values():
            file.beautify()
        self.message.done()
    #
    def copy_all(self,NEW,only_if_changed=True):
        "Copy all the project in a newdir."
        self.message.section("Copy all the project in the directory '%s'..." % NEW)
        for file in self.files:
            newdir = self.files[file].dir.replace(self.ROOT+"/",NEW+"/")
            if not os.path.exists(newdir):
                os.makedirs(newdir)
            self.files[file].write(newdir,file,fd=None,only_if_changed=only_if_changed)
            self.copied += self.files[file].copied
        if self.copied <= 1:
            self.message.write("%d copied file.\n" % self.copied)
        else:
            self.message.write("%d copied files.\n" % self.copied)
        self.message.done()
        #Statistics
        self.message.final()
    #
    def cache_load(self,NEW):
        "Load a cached file for the project in order to decrease the execution of abilint next time"
        cached_name = "%s/.abilint" % NEW
        if os.path.exists(cached_name):
            try:
                self.message.section("Read the cached file '%s'..." % cached_name)
                self.cached = cPickle.load(open(cached_name,"r"))
                self.message.done() 
            except EOFError, cPickle.UnplickingError:
                self.cached = dict()
    #
    def cache_save(self,NEW):
        "Save a cached file for the project in order to decrease the execution of abilint next time"
        for (name,routine) in self.routines.items():
            self.cached[name] = routine.cache_save()
        cached_name = "%s/.abilint" % NEW
        self.message.section("Write the cached file '%s'..." % cached_name)
        cPickle.dump(self.cached,open(cached_name,"w"))
        self.message.done()
    #
    def cache_use(self):
        "Use the information in the cache"
        for routine in self.routines.values():
            routine.cache_use(self)
    #
    def generate(self,pattern_dir='.*'):
        "Add directories and files with a pattern"
        self.message.section("Generate all signature of directories '%s'..." % pattern_dir)
        re_dir = re.compile(pattern_dir)
        for dir in self.dirs:
            if re_dir.match(dir):
                signature = ""
                generated = "_%s_" % dir.split('/')[-1]
                for file in self.dirsfiles[dir]:
                    if file != generated:
                        signature += self.files[file].signature()
                #Add code to the file
                self.files[generated].add_code(signature)
        self.message.done()
    #
    def graph(self,names):
        "Build some graphs of calls"
        #Build a graph dictionary
        if names == "directories":
            filename = "directories.dot"
        else:
            filename = "abinit.dot"
        fd = open(filename,"w")
        if names == "directories":
            #Build the graph of module interdependences
            self.message.section("Build the graphs of all directories...")
            dict_graph = dict()
            #Dictionary of the color of nodes (red, wrong calls, green OK)
            dict_color = dict()
            for routine in self.routines.values():
                dir = routine.dir.split('/')[-1]
                if dir not in dict_graph:
                    dict_graph[dir] = set()
                    dict_color[dir] = "green"
                for called in routine.called:
                    try:
                        called_dir = self.routines[called].dir.split('/')[-1]
                        num = called_dir[0:2]
                        if num == "li":
                            num = called_dir[3:5]
                        dict_graph[dir].add(called_dir)
                        if dir[0:2] < num:
                            dict_color[dir] = "red"
                            self.message.warning("Misplacing: <%s> (%s) calls <%s> (%s)" \
                                    % (routine.name,dir,called,called_dir),verbose=-10)
                    except KeyError:
                        pass
            self.graph_build("directories",fd,dict_graph,dict_color)
        elif names == "all":
            #Build the graph of all routines
            self.message.section("Build the graphs of all routines (parents and children)...")
            names = self.routines.keys()
            names.sort()
            for name in names:
                dict_graph = dict()
                self.message.write("(%s)" % name,verbose=-10,flush=True)
                self.routines[name].graph_children(dict_graph,routines=self.routines)
                self.routines[name].graph_parents(dict_graph,routines=self.routines)
                self.graph_build(name,fd,dict_graph)
        else:
            self.message.section("Build the graph for the routines...")
            for name in names.split(','):
                dict_graph = dict()
                self.message.write("(%s)" % name,verbose=-10,flush=True)
                if name not in self.routines.keys():
                    self.message.error("The routine %s does not exist: no graph\n" % name)
                    return
                self.routines[name].graph_children(dict_graph,routines=self.routines,recursive=True)
                self.routines[name].graph_parents(dict_graph,routines=self.routines,recursive=True)
                self.graph_build(name,fd,dict_graph)
        fd.close()
        self.message.write("(%s)" % filename,verbose=-10)
        self.message.done()
    #
    #Build the tree and give an identifier at each node
    def graph_build(self,name,fd,dict_graph,dict_color=None):
        "Build the graph"
        fd.write("digraph routine_%s {\n" % name)
        fd.write("   node [shape = record,height=.1,style=filled];\n")
        #Build the dict of colors
        if not dict_color:
            dict_color = dict()
            for key in dict_graph.keys():
                dict_color[key] = "white"
                for called in dict_graph[key]:
                    dict_color[called] = "white"
        id = 0
        node_graph = dict()
        for (calling,called_set) in dict_graph.iteritems():
            if calling not in node_graph:
                id += 1
                node_graph[calling] = id
                fd.write(3*' '+'n%d [label="%s",color=%s];\n' \
                    % (id,calling,dict_color[calling]))
            for called in called_set:
                if called not in node_graph:
                    id += 1
                    node_graph[called] = id
                    fd.write(3*' '+'n%d [label="%s",color=%s];\n' \
                        % (id,called,dict_color[called]))
        for (calling,called_set) in dict_graph.iteritems():
            for called in called_set:
                fd.write(3*' '+'n%d -> n%d;\n' % (node_graph[calling],node_graph[called]))
        fd.write("}\n")
    #
    def interfaces_all(self,warning=""):
        "Build the interfaces of all files"
        self.message.section("Build the interfaces...")
        for dir in self.dirs:
            dir_module = "%s/defs" % self.dir_src
            main = os.path.basename(dir)
            if main == "main" or main == "defs":
                continue
            module = "%s%s" % (prefix,main)
            file_module = "%s.F90" % module
            self.add_file(dir_module,file_module,create=True)
            pfile = self.files[file_module]
            i = dir.find("src")
            if i < 0:
                i = dir.find("lib")
            directory = dir[i:]
            pfile.code = head_source % { \
                    'name': module, 
                    'description': "in the directory %s" % directory,
                    'warning': warning }
            #Add interfaces in the file
            list = self.dirsfiles[dir]
            #Sort alphabetically
            list.sort()
            for file in list:
                pfile.code += self.files[file].interface()
            pfile.code += "end module %s\n" % module \
                          + "!!***\n"
        self.message.done()
    #
    def remove_children(self):
        "Remove children of all files"
        for file in self.files.values():
            file.remove_children()
    #
    def remove_code(self,pattern_file):
        "Remove code except comments of files"
        re_file = re.compile(pattern_file)
        for (file,pfile) in self.files.items():
            if re_file.match(file):
                pfile.remove_code()
    #
    def remove_files(self,pattern):
        "Remove files which correspond to the given pattern (Do not clean properly!)"
        self.message.section("Remove the files 'interfaces_xxx'...")
        for file in self.files.keys():
            if pattern in file:
                dir = self.files[file].dir
                self.message.write("[%s/%s]" % (dir,file))
                del self.files[file]
                self.dirsfiles[dir].remove(file)
        self.message.done()
    #
    def set_children_parents(self):
        "Build the list of children and parents routines"
        self.message.section("Determine children and parents routines of all routines...")
        for routine in self.routines.values():
            routine.set_children_parents(self)
        self.message.done()
    #
    def set_functions(self):
        "Build the list of functions"
        self.message.section("Build the list of functions...")
        functions = ""
        self.message.write("\n")
        for routine in self.routines.values():
            if isinstance(routine,Function):
                self.message.write("Function (%s) --> %s\n" % (routine.function_type,routine.name))
                self.functions.append(routine.name)
                functions = "%s|%s" % (functions,routine.name)
        nf = len(self.functions)
        self.message.write("Number of functions: %d\n" % nf)
        if nf  == 0:
            self.re_functions = None
        else:
            self.re_functions = re.compile("(?<![a-z])(%s)[ ]*[(]" % functions[1:],re.IGNORECASE)
        self.message.done()
    #
    def special(self,special_modif):
        "Do some special modifications"
        if len(special_modif) == 0:
            #Nothing to be done
            return
        self.message.write("\nSpecial transformations:\n")
        for name in special_modif:
            self.message.write("[special:%s]" % name)
            eval("self.routines[name].%s" % special_modif[name])
        self.message.write("\n")
    #
    def statistics(self):
        "Display some statistics about the project"
        self.message.write("Statistics of the project %s:\n" % self.name \
            + "%d directories, " % len(self.dirs) \
            + "%d files, " % len(self.files) \
            + "%d modules, " % len(self.modules) \
            + "%d routines (from which %d functions)" % (len(self.routines),len(self.functions)) \
            + " -- [%d files copied]\n" % self.copied,verbose=-10)

#General class handling File and the other ones.
class Structure:
    "Define a structure (a routine, header, etc) which can have a portion of code"
    def __init__(self,name=None,parent=None,message=None):
        "Initialisation"
        self.name = name
        if name:
            self.lower = self.name.lower()
        self.parent = parent
        if parent:
            #Add self in child parent
            self.message = self.parent.message
            #Add to parent's children (if it exists)
            parent.children.append(self)
        elif message:
            self.message = message
        else:
            sys.stderr.write("Structure %s (%s) has no message class!\n" \
                    % (str(self.__class__),self.name))
            sys.exit(1)
        self.children = list()
        self.code = ""
        self.code_backup = ""
    #Add code
    def add_code(self,code):
        "Add code"
        self.code += code
    #Analyze
    def analyze(self):
        "Do nothing"
        pass
    #Backup
    def backup(self):
        "Do a copy of the code"
        self.code_backup = self.code
        for child in self.children:
            child.backup()
    #Beautify
    def beautify(self):
        "Beautify the children  "
        for child in self.children:
            child.beautify()
    #Compare
    def cmp(st1,st2):
        "Compare two structures"
        if st1.lower < st2.lower:
            return -1
        else:
            return 1
    #Check if the code and the backup are identical
    def is_changed(self):
        changed = self.code != self.code_backup
        if changed:
            return True
        #Test the children
        for child in self.children:
            changed = child.is_changed()
            if changed:
                return True
        return False
    #Read
    def read(self,fd):
        "Read from a file descriptor"
        self.code = fd.read()
    #Remove children
    def remove_children(self):
        "Remove all children"
        self.children = list()
    #Replace
    def replace(self,old,new):
        "Replace the regular expressions 'old' by the string 'new'"
        self.code = re.sub(old,new,self.code)
    #Write
    def write(self,fd,only_if_changed):
        "write into a file descriptor"
        if only_if_changed:
            #Check if the code is changed
            if not self.is_changed():
                #Do nothing
                return
        #The code is inserted before the child codes
        fd.write(self.code)
        for child in self.children:
           child.write(fd,only_if_changed)


class File(Structure):
    "Class to define a file (for all files) which is a code with a structure"
    def __init__(self,name,dir=".",read_only=False,message=None):
        "Initialisation"
        Structure.__init__(self,name=name,parent=None,message=message)
        self.dir = dir
        self.copied = 0
        self.read_only = read_only
    #Analyze
    def analyze(self,project=None):
        "Do nothing"
        pass
    #Interface
    def interface(self):
        "Do nothing"
        return ""
    #
    def read(self,fd=None):
        "Read the file."
        if fd == None:
            fd = open("%s/%s" % (self.dir,self.name))
        #Time stamp i.e. the time of the last modification
        self.timestamp = os.path.getmtime(fd.name)
        self.code = fd.read()
        fd.close()
    #
    def write(self,dir,file,fd=None,only_if_changed=True):
        "Write the file as '%s/%s' % (dir,file)."
        #If read_only, return
        if self.read_only:
            return
        #Check if the code is changed
        if only_if_changed:
            if not self.is_changed():
                #Do nothing
                return
        self.message.write("[%s/%s]" % (dir,file))
        if fd == None:
            fd = open("%s/%s" % (dir,file),"w")
        #The code is inserted before the child codes
        fd.write(self.code)
        for child in self.children:
            child.write(fd,only_if_changed=False)
        fd.close()
        self.copied += 1


class File_F90(File):
    "Class to define a fortran90 file which is a code with a structure"
    def __init__(self,name,dir=".",read_only=False,message=None):
        "Initialisation"
        File.__init__(self,name,dir,read_only,message=message)
        self.module = os.path.basename(self.dir)
        self.module = "%s%s" % (prefix,self.module)
    #
    def analyze(self,project):
        "Analyze the code of the file"
        self.message.write("[%s/%s:" % (self.dir,self.name),verbose=10)
        #Create an iterator
        iter_code = iter(self.code.splitlines(1))
        #All the code will be inside child structures
        self.code = ""
        struct = None
        for line in iter_code:
            if re_comment_match.match(line):
                if not isinstance(struct,Comment):
                    struct = Comment(parent=self)
                struct.add_code(line)
            elif re_sub_start.match(line):
                line_lower = line.lstrip()[:10].lower()
                if "subroutine" == line_lower:
                    struct = Routine(parent=self)
                elif "module" in line_lower:
                    struct = Module(parent=self)
                elif "program" in line_lower:
                    struct = Program(parent=self)
                else:
                    struct = Function(parent=self)
                struct.analyze(line,iter_code,project)
            else:
                self.message.fatal("\n%s\n--> No detected routine!\n" % line \
                        + "This part of code can not be parsed as Fortran file:\n" \
                        + "Analysis Error in %s/%s\n" % (self.dir,self.name))
        self.message.write("]\n",verbose=10)
    #
    def analyze_comments(self,project):
        "Analyze comments to detect robodoc headers and footers"
        temp_structs = self.children
        self.children = []
        for struct in temp_structs:
            if isinstance(struct,Comment):
                #Add the structures
                struct.split_comment()
            else:
                #Add the structure
                self.children.append(struct)
        #We have splitted the comments. Now we build robodoc structure 
        #(Robodoc_Header -- [comment] -- Routine -- Robodoc_Footer)
        ro_header = None
        routine = None
        for struct in self.children:
            if isinstance(struct,Robodoc_Header): 
                if ro_header != None:
                    self.message.fatal("\n%s%s" % (ro_header.code,struct.code) \
                            + "Two robodoc headers for a subroutine or a robodoc header without footer!\n" \
                            + "Analysis Error in %s/%s\n" % (self.dir,self.name))
                else:
                    ro_header = struct
            elif isinstance(struct,Robodoc_Footer):
                if routine == None:
                    self.message.warning("Robodoc footer without routine in %s/%s\n" % (self.dir,self.name))
                    if ro_header:
                        ro_header.routine = None
                    else:
                        self.message.fatal("Robodoc footer without header!\n" \
                                + "Analysis error in %s/%s\n" % (self.dir,self.name))
                else:
                    routine = None
                ro_header = None
            elif isinstance(struct,Module):
                if ro_header:
                    routine = struct
                    ro_header.routine = struct
                else:
                    self.message.warning("Routine without robodoc header in %s/%s\n" % (self.dir,self.name))
        #Analyze comment
        for struct in self.children:
            if isinstance(struct,Comment):
                struct.analyze_comment()
    #
    def interface(self):
        "Build the interface"
        code = ""
        for child in self.children:
            if isinstance(child,Routine) or isinstance(child,Function):
                code += child.interface() + "\n"
        return code
    #
    def remove_code(self):
        "Remove code except first comments"
        code = ""
        for line in self.code.splitlines(1):
            if re_comment_match.match(line):
                code += line
            else:
                #We have finished
                break
        self.code = code
    #
    def signature(self):
        "Return all signatures of routines"
        code = ""
        for child in self.children:
            if isinstance(child,Routine) or isinstance(child,Function):
                code += child.signature + "\n"
        return code
    #
    def special(self,modif):
        "Special modifications"
        for child in self.children:
            if isinstance(child,Routine):
                eval("child.%s" % modif)


class File_F90_Generic(File_F90):
    "Special class to generate a generic routine from Fortran90 files"
    def __init__(self,name,dir=".",read_only=False,message=None):
        "Initialisation"
        File_F90.__init__(self,name,dir,read_only,message=message)
        #generic_routine will be associated to the generic routine (see analyze)
        self.generic_routine = None
    #
    def analyze(self,project=None):
        "Analyze the code of the file"
        #First analyze normally the files
        File_F90.analyze(self,project)
        #Add a routine which has the name of the file
        name = self.name.replace(".F90","")
        #Do not add to the list of children of the file
        struct = Generic_Routine(name=name,file=self)
        self.generic_routine = struct
        if project != None:
            project.routines[struct.lower] = struct
    #
    def interface(self):
        "Build a generic interface"
        code = ""
        name = self.name.replace(".F90","")
        for child in self.children:
            if isinstance(child,Routine):
                code_child = child.interface()
                code_child = code_child.replace(indent+"end interface","")
                code_child = code_child.replace("interface","").lstrip()
                code += indent*2 + code_child
        code = "\n!Generic interface of the routines %s\n" % name\
            + indent + "interface %s\n\n" % name\
            + code.rstrip() + "\n\n" \
            + indent + "end interface\n"\
            + "!End of the generic interface of %s\n\n\n" % name
        self.generic_routine.code_interface = code
        return code


class File_F77(File_F90):
    "Class to define a fortran 77 file (always read only)"
    def analyze(self,project):
        "Analyze the code of the file"
        #First, we transform the file into Fortran 90
        #Replace tabulation as blank characters
        self.code = self.code.replace("\t"," "*6)
        code = ""
        for line in self.code.splitlines(1):
            if len(line) < 6:
                continue
            first_char = line[0].lower()
            if first_char == "c" or first_char == "*" or first_char == "!":
                code += "!" + line[1:] 
            elif first_char == "#" or first_char == "$":
                code += "#" + line[1:]
            elif line[5] != " ":
                code = code[:-1] + "&\n" + line[6:] 
            else:
                code += line
        self.code = code
        #Then we analyze as a F90 File
        File_F90.analyze(self,project)


class Code(Structure):
    "Class for fortran code inside Routines or Functions"


class Comment(Structure):
    "Class for comments inside Fortran files"
    def __init__(self,parent):
        "Initialisation"
        Structure.__init__(self,name="comment",parent=parent)
        self.file = self.parent.name
        self.dir = self.parent.dir
    #Detect robodoc header (!***)
    re_robodoc_header = re.compile("!!\*\*\*\*[a-z]\*")
    #
    def analyze_comment(self):
        "Analyze the comment"
        pass
    #
    def split_comment(self):
        "Split the comment to have robodoc header or footer"
        struct = None
        for line in self.code.splitlines(1):
            if "!!" == line[0:2]:
                if "!!***" == line[:5]:
                    if self.re_robodoc_header.match(line):
                        struct = Robodoc_Header(parent=self.parent)
                    else:
                        struct = Robodoc_Footer(parent=self.parent)
                else:
                    if struct == None:
                        struct = Comment(parent=self.parent)
            else:
                if struct == None or isinstance(struct,Robodoc_Header) or isinstance(struct,Robodoc_Footer):
                    struct = Comment(parent=self.parent)
            struct.add_code(line)


class Robodoc_Header(Comment):
    "Class for the robodoc header (comment)"
    #Sections of robodoc
    robodoc_sections = ["NAME", "FUNCTION", "NOTE", "COPYRIGHT", "INPUTS", "OUTPUT", "PARENTS", "CHILDREN", "SOURCE"]
    #
    def __init__(self,parent=None):
        "Initialisation"
        Comment.__init__(self,parent)
        self.sections = dict()
        self.categories = []
        #self.sections = dict()
        #for section in self.robodoc_sections:
        #    self.sections[section] = ""
    #
    def analyze_comment(self):
        "Analyze the robodoc header"
        iter_code = iter(self.code.splitlines())
        #First line
        line = iter_code.next()
        self.type = line[6]
        self.title = line[9:-1]
        category = None
        for line in iter_code:
            sec = line[2:].strip()
            if sec != "" and sec == sec.upper():
                category = sec.upper()
                self.sections[category] = ""
                self.categories.append(category)
            elif category:
                self.sections[category] += "%s\n" % line[2:]
        #Check the coherency between the robodoc header and the routine
        check = True
        if self.type != "m" and isinstance(self.routine,Module) and not isinstance(self.routine,Routine):
            self.message.error("The type for the robodoc header '%s' is not for the module '%s'!\n" \
                    % (self.type,self.routine.name) \
                + "Analysis error in %s/%s\n" % (self.dir,self.file))
        if self.type != "f" and isinstance(self.routine,Routine) and not isinstance(self.routine,Program):
            self.message.error("The type for the robodoc header '%s' is not for the routine '%s'!\n" \
                    % (self.type,self.routine.name)\
                + "Analysis error in %s/%s\n" % (self.dir,self.file))
        if self.type != "p" and isinstance(self.routine,Program):
            self.message.error("The type for the robodoc header '%s' is not for the program '%s'!\n" \
                    % (self.type,self.routine.name) \
                + "Analysis error in %s/%s\n" % (self.dir,self.file))
    #
    def beautify(self):
        "Display Robodoc header (experimental)"
        #Define the type
        if self.routine == None:
            #Do nothing
            return
        elif isinstance(self.routine,Program):
            self.type = "p"
        elif isinstance(self.routine,Routine):
            self.type = "f"
        elif isinstance(self.routine,Module):
            self.type = "m"
        else:
            #Keep the type.
            pass
        #Define the title
        self.title = "ABINIT/%s" % self.routine.name
        if isinstance(self.routine,Routine):
            #Add parents
            line = ""
            routines = list(self.routine.calling)
            routines.sort()
            for name in routines:
                line += name+","
            self.sections["PARENTS"] = line[:-1]+"\n"
            #Add children
            line = ""
            routines = list(self.routine.called)
            routines.sort()
            for name in routines:
                line += name+","
            self.sections["CHILDREN"] = line[:-1]+"\n"
        #Header
        self.code = "!!****%s* %s\n" % (self.type, self.title)
        for section in self.categories:
            self.code += "!! %s\n" % section
            for line in self.sections[section].splitlines(1):
                self.code += "!!%s" % line


class Robodoc_Footer(Comment):
    "Class for the robodoc footer (comment)"


class Module(Code):
    "Fortran module"
    def __init__(self,name=None,parent=None,message=None):
        "Initialisation"
        Structure.__init__(self,name,parent=parent,message=message)
        #No implicit from a higher structure
        self.implicit = None
        if parent:
            self.dir = self.parent.dir
            self.file = self.parent.name
            self.module = self.parent.module
        else:
            self.file = None
            self.dir = None
            self.module = None
    #
    def analyze(self,line,iter_code,project):
        "Analyze the module"
        self.Header = Code(parent=self)
        self.Header.add_code(line)
        #No argument
        self.Header.arguments = list()
        #First line contains the name of the module
        self.name = re_module.match(line).groupdict()['name']
        self.lower = self.name.lower()
        #Add to the list of modules
        project.modules[self.lower] = self
        #Analyze the use statement
        self.Use = Use(parent=self)
        (final_comments,line) = self.Use.analyze(iter_code)
        #Test if implicit statement
        self.Implicit = Implicit(parent=self)
        #Only one line is analyzed
        line = self.Implicit.analyze(line,final_comments)
        #Analyze the declarations
        self.Declaration = Declaration(parent=self,comments=final_comments)
        line = self.Declaration.analyze(line,iter_code)
        #Dictionary of variables
        self.Declaration.analyze_variables(project)
        #Add the code inside a structure Code up to the "contains" statement
        #Normally, only one line (contains)
        struct = Code(parent=self)
        struct.add_code(line)
        #Waiting for the "contains" statement
        for line in iter_code:
            struct.add_code(line)
            if "contains" in line:
                if re_contains.match(line):
                    break
        #Now loop to detect subroutines
        self.analyze_contains(iter_code,project)
    #
    def analyze_contains(self,iter_code,project):
        "Analyze after a contains statement"
        struct = None
        for line in iter_code:
            if re_comment_match.match(line):
                if not isinstance(struct,Comment):
                    struct = Comment(parent=self)
                struct.add_code(line)
            elif re_sub_start.match(line):
                line_lower = line.lstrip()[:10].lower()
                if "subroutine" == line_lower:
                    struct = Routine(parent=self,implicit=self.Implicit)
                else:
                    struct = Function(parent=self,implicit=self.Implicit)
                #Add the modules use in the module to the used modules by the routine
                struct.modules.extend(self.Use.modules)
                #Analyze the code
                struct.analyze(line,iter_code,project)
            elif re_sub_end.match(line):
                #Detect the end of a subroutine or module
                #We have finished
                struct = Code(parent=self)
                struct.add_code(line)
                return
            else:
                self.message.fatal("\n%s\n--> No detected routine!\n" % line \
                        + "This part of code can not be parsed as Fortran file:\n" \
                        + "Analysis Error in %s/%s\n" % (self.dir,self.name))


class Routine(Module):
    "Class to handle subroutines or function"
    #Ampersand (continuation line)
    re_amp = re.compile('[ ]*&[ ]*')
    #Detect optional arguments
    re_optional = re.compile("optional.*::", re.IGNORECASE)
    def __init__(self,name=None,parent=None,implicit=None,message=None):
        "Initialisation"
        Module.__init__(self,name,parent=parent,message=message)
        self.nature = "subroutine"
        self.called = set()
        self.calling = set()
        #No interface defined for this routine (call self.interface to do that)
        self.has_interface = False
        self.optional = False
        #Signature (header+arguments) of the routine
        self.signature = ""
        #List of used modules
        self.modules = list()
        #Use implicit (if inside module)
        self.implicit = implicit
        #Gestion of the cache
        #1 - Time stamp
        if self.parent:
            self.timestamp = self.parent.timestamp
        else:
            self.timestamp = 0
        #2 - The information of the cache is updated
        self.cached_updated = False
        #3 - called routines
        self.cached_called = None
        #4 - arguments
        self.cached_arguments = None
        #5 - calling
        self.cached_calling = None
    #
    def add_use_interface(self,project):
        "Add 'use self.module'"
        modules = set()
        #Add routines and functions
        functions = list()
        else_modules = set()
        for called in self.called:
            #For generic or optional routines
            if called in interfaces_routines.keys():
                else_modules.add(interfaces_routines[called])
            try:
                routine = project.routines[called]
            except KeyError:
                if called in intrinsic_routines:
                    #Next routine
                    continue
                else:
                    #Next routine
                    self.message.warning_no_interface(self.dir,self.file,self.name,called)
                    continue
            modules.add(routine.module)
            if isinstance(routine,Generic_Routine):
                #Use generic routine
                generic_routines = True
            if isinstance(routine,Function):
                functions.append(routine)
        #Remove main (for the program "optic" which has some subroutines !!!)
        if "main" in modules:
            modules.remove("main")
        #Build a list in order to sort
        lmodules = list(modules)
        lmodules.sort()
        emodules = list(else_modules)
        emodules.sort()
        #Modify the interfaces
        self.Use.add_use_interface(lmodules,emodules)
        #Add declarations of functions
        if functions:
            #Sort functions (to have unique ordering)
            functions.sort(Structure.cmp)
            self.Declaration.add_functions(functions)
    #
    def analyze(self,line,iter_code,project):
        "Iterate the iterator up to the end of the routine"
        #We are already inside a routine
        #We have the first line of header
        self.Header = Header_Routine(parent=self)
        #One line is not enough: Add line if continuation line
        self.Header.analyze(line,iter_code)
        #Add the routine to the project
        project.add_routine(self)
        #Add the cached information
        self.cache_use(project)
        #Analyze the use statement
        self.Use = Use(parent=self)
        (final_comments,line) = self.Use.analyze(iter_code)
        #Add the modules
        self.modules.extend(self.Use.modules)
        #Test if implicit statement
        self.Implicit = Implicit(parent=self)
        #Only one line is analyzed
        line = self.Implicit.analyze(line,final_comments)
        #Analyze the declarations
        self.Declaration = Declaration(parent=self,comments=final_comments)
        line = self.Declaration.analyze(line,iter_code)
        #Analyze the body
        self.Body = Body(parent=self)
        self.Body.analyze(line,iter_code,project)
    def analyze_body(self,project):
        "Analyze the body of the routine"
        #We build the dictionary of all variables
        self.Declaration.analyze_variables(project)
        #Then analyze the body
        self.Body.analyze_body(self.Declaration.dict_vars)
    #
    def cache_save(self):
         "Save some information for the cache"
         return { "timestamp": self.timestamp,
                  "called": self.called,
                  "arguments": self.arguments,
                  "calling": self.calling }
    #
    def cache_use(self,project):
        "Use the cached information"
        #Test time stamp to save time
        if project.cached.has_key(self.lower):
            cached = project.cached[self.lower]
            if cached["timestamp"] == self.timestamp:
                self.cached_updated = True
                self.cached_called = cached["called"]
                self.cached_arguments = cached["arguments"]
                self.cached_calling = cached["calling"]
            else:
                #The cache is not updated. Nevertheless,we use some information
                self.cached_updated = False
                self.cached_called = cached["called"]
                self.cached_calling = cached["calling"]
    #
    def get_arguments(self,project):
        "Build the list of arguments"
        #Arguments
        if self.cached_arguments != None:
            self.arguments = self.cached_arguments
        else:
            self.arguments = self.Declaration.arguments(self.Header.arguments,self.Implicit.dict,project)
    #
    def beautify(self):
        "Beautify the code of the routines (experimental)"
        #Remove tabulations
        self.code = self.code.replace("\t",indent)
        #Format the comment "!Arguments"
        self.code = re_arguments.sub(self.code,"\n\n" + comment_arguments)
        #Format the comment "!Local variables"
        self.code = re_local_variables.sub(self.code,"\n\n" + comment_local_variables)
    #
    def graph_children(self,dict_graph,routines=list(),recursive=False):
        "Add children in the graph"
        calling = self.name
        if calling in dict_graph:
            #Already done: To avoid Recursive call, return
            return
        else:
            dict_graph[calling] = set()
        for name in self.called:
            try:
                struct = routines[name]
                dict_graph[calling].add(struct.name)
                if recursive:
                    struct.graph_children(dict_graph,routines=routines,recursive=True)
            except KeyError:
                dict_graph[calling].add(name)
    #
    def graph_parents(self,dict_graph,routines=list(),recursive=False):
        "Add parents in the graph"
        for name in self.calling:
            #Add the parents routines
            try:
                struct = routines[name]
                calling = struct.name
                if calling not in dict_graph:
                    dict_graph[calling] = set()
                called = self.name
                if called in dict_graph[calling]:
                    #Already done: To avoid recursive call, continue
                    continue
                dict_graph[calling].add(called)
                if recursive:
                    struct.graph_parents(dict_graph,routines=routines,recursive=True)
            except KeyError:
                pass
    #
    def interface(self):
        "Build the interface"
        if self.arguments == "":
            #Nothing to be detected
            self.has_interface = False
            return ""
        #Header (desindent inside a continuation line)
        header = self.re_amp.sub('&'+indent*2,self.Header.code.lstrip())
        #Check if an optional argument is present
        if self.re_optional.search(self.arguments):
            self.optional = True
        code = self.arguments
        #Indentation of the interface
        code = re_indent.sub(indent*3,code).rstrip() + "\n"
        #Store the interface without interface...end interface
        self.signature = indent*2 + header \
                + code\
                + indent*2 + "end %s %s\n" % (self.nature,self.name)
        code = indent + "interface\n" \
             + self.signature\
             + indent + "end interface\n"
        #Store the interface for this routine
        self.code_interface = code
        self.has_interface = True
        return code
    #
    def set_children_parents(self,project):
        "Create the list of children and parents of the routines"
        #Save time if called is already done
        if self.cached_calling != None:
            #Even it is not updated, we use this information
            self.calling = self.cached_calling
        if self.cached_updated:
            self.called = self.cached_called
        else:
            #Define the called routines
            list = re_call.findall(self.Body.code)
            for tt in list:
                called = tt[1].lower()
                self.called.add(called)
            #Add functions
            if project.re_functions:
                list = project.re_functions.findall(self.Body.code)
                functions = set()
                for tt in list:
                    called = tt.lower()
                    if called not in intrinsic_routines and called != self.name:
                        #Add the functions
                        functions.add(called)
                self.called.update(functions)
            #Now change the calling routines of other routines.
            if self.cached_called:
                called_to_remove = self.cached_called.difference(self.called)
                #Remove self into calling routines which are not already called
                for called in called_to_remove:
                    if project.routines.has_key(called):
                        if self.lower in project.routines[called].calling:
                            project.routines[called].calling.remove(self.lower)
                #New called routine to add
                called_to_add = self.called.difference(self.cached_called)
            else:
                called_to_add = self.called
            #Add self into calling routines for called
            for called in called_to_add:
                if project.routines.has_key(called):
                    project.routines[called].calling.add(self.lower)
                #elif called in intrinsic_routines:
                #    #Do nothing
                #    pass
                #else:
                #    self.message.warning_no_reference(self.dir,self.file,self.name,called)
        #Verbose message
        #text = "(%s:" % self.name
        #for called in self.called:
        #    text += "{%s}" % called
        #self.message.write(text+")")
        self.message.write("%s\n" % self.name)


class Generic_Routine(Routine):
    "Class to handle generic routine"
    def __init__(self,name,file):
        "Initialisation"
        Routine.__init__(self,name,parent=None,message=file.message)
        #A generic routine is not the child of a file but it is related to a file.
        #Initialisation done
        self.generic_file = file
        self.file = file.name
        self.dir = file.dir
        self.module = file.module
    def add_use_interface(self,project):
        "Do nothing"
        return
    def get_arguments(self,project):
        "Do nothing"
        self.arguments = ""
        return
    def interface(self):
        "Do nothing"
        return ""
    def set_children_parents(self,project,called=None):
        "Create the list of children and parents routines"
        for routine in self.generic_file.children:
            if isinstance(routine,Routine):
                self.called.update(routine.called)


class Function(Routine):
    "Class to handle Function"
    def __init__(self,name=None,parent=None,implicit=None,message=None):
        "Initialisation"
        Routine.__init__(self,name,parent,implicit,message)
        self.nature = "function"
    #
    def analyze(self,line,iter_code,project):
        "Analyze the function"
        self.Header = Header_Function(parent=self)
        self.Header.analyze(line,iter_code)
        #Add in the dictionary of all routines
        project.add_routine(self)
        #Add the cached information
        self.cache_use(project)
        #Analyze the use statement
        self.Use = Use(parent=self)
        (final_comments,line) = self.Use.analyze(iter_code)
        #Add the modules
        self.modules.extend(self.Use.modules)
        #Test if implicit statement
        self.Implicit = Implicit(parent=self)
        line = self.Implicit.analyze(line,final_comments)
        #Analyze the declarations
        self.Declaration = Declaration(parent=self,comments=final_comments)
        line = self.Declaration.analyze(line,iter_code)
        #Analyze the body
        self.Body = Body(parent=self)
        self.Body.analyze(line,iter_code,project)
    #
    def get_arguments(self,project):
        "Build the list of arguments"
        #Arguments
        self.arguments = self.Declaration.arguments(self.Header.arguments,self.Implicit.dict,project)
        if not self.has_type:
            #Search the type func_name
            if not self.Declaration.dict_vars.has_key(self.func_name):
                self.message.fatal("\n%s/%s: Check if the arguments are well formatted:\n" \
                    % (self.parent.dir,self.parent.name) \
                    + "Type not declared for the function '%s'\n" % self.name)
            else:
                #We are looking for self.func_name in dict_vars
                self.function_type = self.Declaration.dict_vars[self.func_name][0]
                self.has_type = True


class Program(Routine):
    "Fortran program (as a routine)"
    def interface(self):
        "No interface for program"
        self.code_interface = ""
        self.has_interface = False
        return ""

class Header_Routine(Code):
    "Header of a routine"
    #Commment inside a line
    re_comment = re.compile("[!].*")
    #Detect the beginning of a block (program, subroutine, function)
    re_startblock = re.compile('(?P<header>[ \t]*' \
        + '(?P<type>(program|subroutine|(([^!\n]*?)function)))' \
        + '[ ]*(?P<name>\w+)[ ]*(?P<arguments>[(][^)]*[)])?[^\n]*)\n', \
           re.MULTILINE+re.IGNORECASE)
    #In arguments, remove some characters
    re_inarg = re.compile('[ \t\n&()]+')
    #
    #Analyze the code
    def analyze(self,line,iter_code):
        "Analyze the header"
        self.add_code(line)
        while re_continuation.search(line):
            line = iter_code.next()
            self.add_code(line)
        #Analyze the header
        search = self.re_startblock.match(self.code).groupdict()
        self.parent.type = search['type']
        self.parent.name = search['name']
        args = search['arguments']
        if args == '()':
            args = None
        if args:
            #Remove comments
            args = self.re_comment.sub('',args)
            self.arguments = self.re_inarg.sub('',args)
            self.arguments = self.arguments.lower().split(',')
        else:
            self.arguments = list()
        self.parent.lower = self.parent.name.lower()
        self.message.write("<%s>" % self.parent.name)


class Header_Function(Header_Routine):
    "Header of a function"
    def analyze(self,line,iter_code):
        "Analyze the header"
        Header_Routine.analyze(self,line,iter_code)
        #Determine the type of the function
        self.parent.has_type = len(self.parent.type) > 9
        if self.parent.has_type:
            #'xxx function'
            self.parent.function_type = self.parent.type[:-9].strip()
        else:
            #Determine inside the code
            #First check if result and use the corresponding name
            search = re_result.search(self.code)
            if search:
                self.parent.func_name = search.groupdict()['result']
            else:
                self.parent.func_name = self.parent.name
            #Add at the beginning of the list of arguments
            self.arguments.insert(0,self.parent.func_name)


class Use(Code):
    "Use statement"
    re_in_use = re.compile('^[ \t]*use ',re.IGNORECASE)
    #Multiple (more than 3 \n): bug or unavailable
    re_multi_n = re.compile("[\n]{3,}")
    #Use statement
    re_use = re.compile('^[ ]+use[ ]*(?P<name>\w+)', \
                       re.MULTILINE+re.IGNORECASE)
    re_use_prefix = re.compile('^[ ]+use[ ]*'+prefix+'.*?\n', \
                       re.MULTILINE+re.IGNORECASE)
    #
    #Add use
    def add_use_interface(self,modules,else_modules):
        "Add 'use interfaces_xxx'"
        #Remove section created by abilint
        self.code = re_section_abilint.sub('',self.code)
        if re_section_abilint_start.search(self.code):
            self.message.error("Routine %s: alone header of an abilint section" % self.parent.name)
            #Remove the beginning of a section created by abilint (if separated)
            self.code = re_section_abilint_start.sub('',self.code)
        if re_section_abilint_end.search(self.code):
            self.message.error("Routine %s: alone footer of an abilint section" % self.parent.name)
            #Remove the end of a section created by abilint (if separated)
            self.code = re_section_abilint_end.sub('',self.code)
        #Remove line with use prefix
        self.code = self.re_use_prefix.sub('',self.code)
        #Remove some replaced use
        self.code = re_use_replaced.sub('',self.code)
        #Remove empty preprocessing directives
        self.code = re_empty_preproc.sub('',self.code)
        #Remove multiple \n
        self.code = self.re_multi_n.sub('\n',self.code)
        #Add preprocessing commands, comments and implicit none
        if modules or else_modules:
            text_use = "\n\n" + use_before
            #Add modules
            for module in modules:
                if prefix in module:
                    if module == self.parent.module and self.parent.has_interface:
                        #We except the given subroutine
                        text_use += indent + "use %s, except_this_one => %s\n" \
                               % (self.parent.module,self.parent.name)
                    else:
                        text_use += indent + "use %s\n" % module
            if else_modules:
                text_use += "#else\n"
                for name in else_modules:
                    if self.parent.name in interfaces_routines.keys() \
                            and interfaces_routines[self.parent.name] == name:
                        text_use += indent + "use %s, except_this_one => %s\n" \
                                % (name,self.parent.name)
                    else:
                        text_use += indent + "use %s\n" % name
            text_use += use_after + "\n"
            #Add text_use inside use statements
            self.code += text_use
    #
    #Analyze the code
    def analyze(self,iter_code):
        """Analyze use statements (special treatment for preprocessing commands
           which needs to be clarify)"""
        #List of used modules
        self.modules = list()
        comments = ""
        inside_if = False
        for line in iter_code:
            if self.re_in_use.match(line):
                #Add comments and the line
                self.add_code(comments+line)
                #Add this module
                self.modules.append(line.split()[1])
                comments=""
            elif re_comment.match(line):
                if "#if" in line:
                    inside_if = True
                elif "#endif" in line:
                    inside_if = False
                comments += line
            else:
                #We have finished
                final_comments = ""
                if inside_if:
                    #We remove the beginning of the preprocessing directives
                    a_comments = comments.split("\n")
                    #Last element is not a line
                    a_comments.pop()
                    a_comments.reverse()
                    comments = ""
                    l_final = len(a_comments)
                    for cline in a_comments:
                        if "#if" in cline:
                            l_final = 2
                        if l_final > 0:
                            final_comments = cline + "\n" + final_comments
                        else:
                            comments = cline + "\n" + comments
                        l_final -= 1
                if comments:
                    self.add_code(comments)
                return (final_comments,line)


class Implicit(Code):
    "Class for the statement 'implicit'"
    re_imp_stat = re.compile("(?P<type>.*)[ ]*[(](?P<range>[^)]+)[)]")
    default_dict = dict()
    #Default in Fortran
    for i in range(97,105):
        #real
        default_dict[chr(i)] = "real"
    for i in range(105,111):
        #integer
        default_dict[chr(i)] = "integer"
    for i in range(111,123):
        #real
        default_dict[chr(i)] = "real"
    #Detect an implicit statement
    def analyze(self,line,comments=""):
        "Test if implicit is present"
        if "implicit" in line.lower():
            if comments:
                #Fatal error
                self.message.fatal("%s/%s[%s]: " % (self.parent.dir,self.parent.file,self.parent.name) \
                    + "Preprocessing block not closed for use statements\n" )
            self.code = line
            self.implicit()
            return ""
        elif self.parent.implicit:
            self.dict = self.parent.implicit.dict
            return line
        else:
            #Error
            self.implicit()
            self.message.error("No implicit statement in %s (%s/%s)" \
                % (self.parent.name,self.parent.dir,self.parent.file))
            return line
    #Build a dictionary
    def implicit(self):
        "Give the type of variables given by implicit statement"
        line = self.code.lower()
        if "none" in line:
            self.dict = None
            return
        #Default implicit in Fortran
        self.dict = self.default_dict
        if self.code == "":
            #Finished
            return
        #We analyze the implicit statement
        line = line.replace('implicit','').strip()
        search = self.re_imp_stat.match(line)
        if search:
            search = self.re_imp_stat.match(line).groupdict()
        else:
            self.message.fatal("%s/%s[%s]: " % (self.parent.dir,self.parent.file,self.parent.name) \
                + "%s" % line \
                + "Analysis error of implicit statement\n" )
        type_imp = search["type"]
        #a-h,...
        table = search["range"].split(",")
        for letters in table:
            (a1,a2) = letters.split('-')
            a1 = ord(a1)
            a2 = ord(a2)+1
            for i in range(a1,a2):
                self.dict[chr(i)] = type_imp
        return


class Declaration(Code):
    "Declaration statement"
    #Comment (at the end of a line)
    re_comment = re.compile('[!].*')
    #Detect declarations
    re_declaration = re.compile('^[ \t]*' \
        + '(character|complex|data|dimension|double|end[ ]+type|external|'\
        + 'include|integer|intrinsic|logical|private|public|real|save|type)', re.IGNORECASE)
    #Detect "type " or "type," or "type::"
    re_def_type = re.compile('^[ \t]*type[ ]*(?![(])',re.IGNORECASE)
    #Detect digits only (1.2d0 or 1.3e-4 etc.)
    re_digits = re.compile("^\d+[.]?\d*[de]?[+-]?[\d]*$")
    #Detect digits at the beginning for 0.0_dp
    re_digits_start = re.compile("^\d+[.]?\d*[de]?[+-]?[\d]*_")
    #Detect #include
    re_diesis_include = re.compile('''[#]include[ ]*[<'"](?P<file>[\w./_-]+)[>'"]''')
    #Remove elements of type
    re_element = re.compile("%\w+")
    #Detect "end type"
    re_end_type = re.compile('[ \t]*end[ ]+type',re.IGNORECASE)
    #Detect a group after =
    re_equal = re.compile('[ ]*=.*')
    #Include command
    re_include = re.compile('^[ ]*include.*?\n',re.MULTILINE+re.IGNORECASE)
    #Multiple \n
    re_mn = re.compile('\n+',re.MULTILINE)
    #No letter
    re_noletter = re.compile("\W+")
    #No ampersand
    re_noamp = re.compile("[ ]*[&]+[ ]*")
    #Detect only declarations without include, data, external and save
    re_only_declaration = re.compile('^[ \t]*' \
        + '(character|complex|dimension|double|end[ ]+type|integer|logical|private|public|real|type)',\
        re.IGNORECASE)
    #Detect a group (.*)
    re_paren = re.compile('[(][^)]*[)]')
    #For character(len=xxx)
    re_character = re.compile('character[(](len[ =]+)?(?P<len>[^)]+)[)]',re.IGNORECASE)
    #For complex(kind=dp) or complex(kind(dp))
    re_complex = re.compile('complex[(](kind[=(])?(?P<kind>[^)]+)[)]+',re.IGNORECASE)
    #Detect !Local
    re_local = re.compile("!Local",re.IGNORECASE)
    #For real(kind=dp) or real(kind(dp))
    re_real = re.compile('real[(](kind[=(])?(?P<kind>[^)]+)[)]+',re.IGNORECASE)
    #For type(xxx)
    re_type = re.compile('type[(](?P<type>[^)]+)[)]',re.IGNORECASE)
    #Detect variable var(.*) and also xxx=
    #re_var = re.compile('(\w+([(][^)]+[)])?)')
    re_var = re.compile('(\w+[ ]*((=[^,]+)|([(][^)]+[)])?))')
    #
    def __init__(self,name=None,parent=None,comments=""):
        "Initialisation"
        Code.__init__(self,name,parent)
        self.code = comments
        #Dictionary of all variables
        self.dict_vars = None
    #
    #Add functions in the declaration
    def add_functions(self,functions):
        "Add declaration of function with preprocessing"
        #Remove section created by abilint
        self.code = re_section_abilint.sub('\n',self.code)
        if re_section_abilint_start.search(self.code):
            self.message.error("Routine %s: alone header of an abilint section" % self.parent.name)
            #Remove the beginning of a section created by abilint (if separated)
            self.code = re_section_abilint_start.sub('',self.code)
        if re_section_abilint_end.search(self.code):
            self.message.error("Routine %s: alone footer of an abilint section" % self.parent.name)
            #Remove the end of a section created by abilint (if separated)
            self.code = re_section_abilint_end.sub('',self.code)
        #We remove the declaration of the function
        for struct in functions:
            self.code = re.sub("(.*::.*),?%s(?=[, \n])" % struct.name, r"\1",self.code)
        #We remove a comma behind "::"
        self.code = re.sub( "::[ ]*,", ":: ",self.code)
        #We remove a line ending by "::" with a comma or not behind
        self.code = re.sub( ".*::[ ]*[,]*[ ]*\n", "",self.code)
        text_functions = ""
        for struct in functions:
            text_functions += indent + "%s :: %s\n" % (struct.function_type,struct.name)
        #Add at the end of declaration
        if re_end_variables.search(self.code):
            #Add before "!******"
            self.code = re_end_variables.sub("\n" + notuse_before + text_functions \
                    + notuse_after + end_variables,self.code)
        else:
            self.code += notuse_before + text_functions + notuse_after + "\n"
    #
    #Analyze the code
    def analyze(self,line,iter_code):
        "Analyze the declaration statements"
        if not line:
            line = iter_code.next()
        while True:
            if self.re_declaration.search(line):
                self.add_code(line)
                #Is it a continuation line or a comment line inside continuations lines?
                while re_continuation.search(line) or re_comment_match.match(line):
                    line = iter_code.next()
                    self.add_code(line)
            elif re_comment_match.match(line):
                self.add_code(line)
            else:
                #We have finished
                #Test if not implicit
                if "implicit" in line:
                    self.message.fatal("[%s] Implicit statement '%s' inside declaration of variables" \
                            % (self.parent.name,line[:-1]) \
                            + "\nPossible reason: An include statement before implicit statement\n")
                return line
            try:
                line = iter_code.next()
            except StopIteration:
                self.message.fatal("[%s] End of the code inside declaration!!\n" % self.parent.name)
    #
    #Analyze all variables xxxx :: var -> dict[var] == xxx
    def analyze_variables(self,project):
        "Analyze all variables xxxx :: var -> dict[var] == xxx"
        #First, we build a long line without continuation, comments and interface
        iter_code = iter(self.code.splitlines())
        code = list()
        for line in iter_code:
            if self.re_only_declaration.search(line):
                line = self.re_comment.sub('',line)
                code.append(line)
                while re_continuation.search(line+"\n"):
                    null_line = True
                    #Detect if comment lines inside continuation lines
                    while null_line:
                        line = iter_code.next()
                        line = self.re_comment.sub('',line)
                        null_line = len(line) == 0
                    code[-1] += line
                #Remove ampersand
                code[-1] = self.re_noamp.sub('',code[-1])
        #First we assume to have ::
        dict_vars = dict()
        iter_code = iter(code)
        for line in iter_code:
            #Build: declaration (decl) :: list of variables  
            if "::" in line:
                ll = line.split('::')
                if len(ll) != 2:
                    self.message.fatal("\n%s/%s: [%s]\n--> Declaration is not correct: '%s'" \
                            % (self.parent.dir,self.parent.file,self.parent.name,line[:-1]) \
                            + "\nToo many '::'. Do not use ';' in declaration.\n")
                else:
                    (decl,liste) = ll 
            else:
                #In this case: keyword variables
                liste = line.strip().split()
                decl = liste[0]
                decl_lower = decl.lower()
                if decl_lower == "double":
                    #Add 'precision' or 'complex'
                    decl += " " + liste[1]
                    liste = reduce(lambda x,y: x + " " + y, liste[2:])
                elif len(liste) == 1:
                    #Assume that it is 'private' or 'public'
                    continue
                else:
                    liste = reduce(lambda x,y: x + " " + y, liste[1:])
            decl = decl.strip()
            liste = liste.strip()
            #Definition of a type?
            if self.re_def_type.match(decl):
                #Go to "end type"
                while not self.re_end_type.match(line):
                    line = iter_code.next()
            #Check if parameter
            for (name,var) in split_variables(liste):
                if dict_vars.has_key(name):
                    #Already a statement (maybe dimension ??)
                    if "dimension" == decl:
                        #We keep the first declaration: dimension var(xx)
                        dict_vars[name] = (dict_vars[name][0],var)
                    else:
                        #We add decl
                        dict_vars[name] = ("%s, %s" % (dict_vars[name][0],decl),var)
                elif "dimension" == decl:
                    #We do not use
                    dict_vars[name] = ("",var)
                else:
                    dict_vars[name] = (decl,var)
        #Remove private variables
        for (name,(decl,var)) in dict_vars.items():
            if "private" in decl.lower():
                del(dict_vars[name])
        self.dict_vars = dict_vars
    #
    #Give the declaration of arguments
    def arguments(self,arguments,dict_implicit,project):
        "Give declarations of arguments"
        #We analyze all the variables
        self.analyze_variables(project)
        #Set for parameters in argument (ex: selected_kind)
        parameters = set()
        #Set for modules in parameters
        modules = set()
        #List of declarations
        declarations = list()
        include_directive = ""
        names = set()
        for argument in arguments:
            in_include_file = False
            arg_lower = argument.lower()
            if self.dict_vars.has_key(arg_lower):
                (type_arg,arg) = self.dict_vars[arg_lower]
                #Test if correct:
                if type_arg.count("real") > 1 or type_arg.count("integer") > 1 or type_arg.count("intent") > 1:
                    self.message.fatal("[%s/%s:%s]:" % (self.parent.dir,self.parent.file,self.parent.name) \
                        + " Argument '%s' has a declaration which depends on preprocessing directives\n" % arg \
                        + "Do not use preprocessing directives for argument\n")
            else:
                type_arg = ""
                arg = ""
            if type_arg == "":
                #We use the implicit
                if dict_implicit == None:
                    #Check if not a #include directives
                    #Not really elegant but works
                    if "#include" in self.code:
                        file = self.re_diesis_include.search(self.code).groupdict()['file']
                        if project.files.has_key(file):
                            if argument in project.files[file].code:
                                in_include_file = True
                                include_directive = "#include <%s>\n" % file
                                arg = ""
                            else:
                                in_include_file = False
                        else:
                            in_include_file = False
                    else:
                        in_include_file = False
                    if not in_include_file:
                        text = "\nArguments of the routine '%s':" % self.parent.name
                        for arg in arguments:
                            text += " '%s'" % arg
                        text += "\n==Code"+"="*50+">\n%s" % self.code + "="*56+"<\n"
                        self.message.fatal( \
                            text + "[%s/%s:%s] Argument {%s} is not declared\n" \
                            % (self.parent.dir,self.parent.file,self.parent.name,argument))
                else:
                    type_arg = dict_implicit[arg_lower[0]]
                    if arg == "":
                        arg = argument
            #Remove space to look for "xxx("
            type_arg_lower = type_arg.lower().replace(' ','')
            #Check if type_arg does not depend on a parameter or a module (ex: real(ddp) or type(bidon))
            if "character(" in type_arg_lower:
                name = self.re_character.match(type_arg_lower).groupdict()["len"]
            elif "complex(" in type_arg_lower:
                name = self.re_complex.match(type_arg_lower).groupdict()["kind"]
            elif "real(" in type_arg_lower:
                name = self.re_real.match(type_arg_lower).groupdict()["kind"]
            elif "type(" in type_arg_lower:
                name = self.re_type.match(type_arg_lower).groupdict()["type"]
            else:
                name = None
            if name and not (self.re_digits.match(name) or name == "*"):
                #if not a number : Add name without digits (ex: 0.d0_dp)
                name = self.re_digits_start.sub('',name)
                names.add(name)
            #Store as a scalar or an array
            if "dimension" in type_arg_lower:
                #Check if it depends on a parameter
                dims = arg.replace("dimension(","").lower()
                is_array = True
            elif argument+"(" in arg.lower():
                #Check if it depends on a parameter
                dims = arg.replace(argument+"(","").lower()
                is_array = True
            elif in_include_file:
                #Do not append (append #include)
                is_array = False
            elif "integer" in type_arg:
                #Insert first the integer 
                declarations.append(("i",arg,type_arg))
                is_array = False
            elif "integer" in type_arg:
                #Insert first the integer 
                declarations.append(("i",arg,type_arg))
                is_array = False
            else:
                declarations.append(("s",arg,type_arg))
                is_array = False
            if is_array:
                #Remove elements of type
                dims = self.re_element.sub("",dims)
                #Split into dimensions
                dims = self.re_noletter.sub(" ",dims).split()
                has_name = False
                for dim in dims:
                    if self.re_digits.match(dim):
                        #Is a number
                        pass
                    elif dim == "max" or dim == "min":
                        pass
                    else:
                        names.add(dim)
                        if dim in arguments:
                            has_name = True
                if has_name:
                    declarations.append(("zz",arg,type_arg))
                else:
                    declarations.append(("z",arg,type_arg))
        #For each variables which depend arguments, we add information
        for name in names:
            #Find where the information about 'name' is stored
            has_name = False
            if name in arguments:
                #We have already
                has_name = True
            elif self.dict_vars.has_key(name):
                parameters.add(self.dict_vars[name])
                has_name = True
            else:
                for module in self.parent.modules:
                    if project.modules.has_key(module):
                        dict_vars = project.modules[module].Declaration.dict_vars
                        if dict_vars.has_key(name):
                            modules.add(module)
                            has_name = True
                            break
            if not has_name:
                texte = "\nModules:"
                for module in self.parent.modules:
                    texte += " %s" % module
                texte += "\nArguments of the routine '%s':" % self.parent.name
                for argu in arguments:
                    texte += " '%s'" % argu
                self.message.fatal("%s\n[%s/%s:%s]:" % (texte,self.parent.dir,self.parent.file,self.parent.name) \
                        + " The argument '%s' depends on '%s' which is not found in the declaration even in a module\n" \
                        % (arg,name))
        #Build the declaration of arguments
        decl_args = ""
        #Sort arguments (integer, scalar, arrays)
        declarations.sort()
        #Add modules
        for module in modules:
            decl_args += "use %s\n" % module
        #Add include directive (only one)
        decl_args += include_directive
        #Add parameters
        for param in parameters:
            decl_args += "%s :: %s\n" % param
        for (a,arg,type_arg) in declarations:
            line = "%s :: %s\n" % (type_arg,arg)
            if len(line) > 100:
                #Split the line
                line = line[:60] + line[60:].replace(',',', &\n&'+9*' ',1)
            decl_args += line
        return decl_args


class Body(Code):
    "The body of the routine or a function"
    #Comment at the end of a line
    re_comment_line = re.compile('[!].*\n')
    #Detect interface
    re_interface_start = re.compile('^[ \t]*interface',re.IGNORECASE)
    re_interface_end   = re.compile('^[ \t]*end[ ]+interface',re.IGNORECASE)
    #Character string
    re_string = re.compile('''['"][^'"]*['"]''')
    #
    def analyze(self,line,iter_code,project):
        "Iterate the iterator up to the end of the routine."
        self.code = ""
        while True:
            self.code += line
            line_lower = line.lstrip()[:8].lower()
            if "interfac" == line_lower:
                if self.re_interface_start.match(line):
                    #We enter in an interface block
                    self.analyze_interface(iter_code)
            elif "contains" == line_lower:
                if re_contains.match(line):
                    Module.analyze_contains(self.parent,iter_code,project)
                    #The end is detected 
                    return
            elif "end" == line_lower[:3]:
                if re_sub_end.match(line):
                    #Detect the end of the subroutine: We have finished
                    return
            try:
                line = iter_code.next()
            except StopIteration:
                self.message.fatal("\n%s\n--> No detected end of the routine!\n" % line\
                        + "Analysis Error in %s/%s\n" % (self.parent.dir,self.parent.file))
    #
    def analyze_body(self,dict_vars):
        "Analyze the body (initialization, unused variables)"
        if dict_vars == None:
            self.message.fatal("No dictionary of variables\n" \
                    + "Analysis Error in %s/%s\n" % (self.parent.dir,self.parent.file))
        #We have a dictionary
        a = split_code(self.code)
        uninitialized = dict_vars.keys()
        unused = dict_vars.keys()
        #First, we build a long line without continuation, comments
        iter_code = iter(self.code.splitlines(1))
        code = list()
        for line in iter_code:
            if re_comment_match.match(line):
                #It is a comment line
                continue
            #Remove comment at the end of line
            line = self.re_comment_line.sub('',line)
            code.append(line)
            while re_continuation.search(line+"\n"):
                null_line = True
                #Detect if comment lines inside continuation lines
                while null_line:
                    line = iter_code.next()
                    line = self.re_comment_line.sub('',line)
                    null_line = len(line) == 0
                code[-1] += line
            #Split for ;
            a = code[-1].split(";")
            if len(a) > 1:
                code.pop()
                code.extend(a)
            #Replace '==' by .eq.
            #if '==' in code[-1]:
            #    code[-1] = code[-1].replace('==','.eq.')
        #for line in code:
        #    if "=" in line:
        #        var = line.split('=')[0]
        #        print var,line,
        print code
        sys.exit(1)
    #
    def analyze_interface(self,iter_code):
        "Iterate inside an interface"
        in_interface = 1
        while in_interface > 0:
            try:
                line = iter_code.next()
            except StopIteration:
                self.message.fatal("\n%s\n--> No detected end of the interface!\n" % line\
                                + "Analysis Error in %s/%s\n" % (self.parent.dir,self.parent.file))
            self.code += line
            line_lower = line.lower()
            if "interface" in line_lower:
                if self.re_interface_end.match(line):
                    in_interface -= 1
                elif self.re_interface_start.match(line):
                    in_interface += 1


class FatalError(Exception):
    "Error raised when Fatal message"
    def __init__(self,value):
       self.value = value
    def __str__(self):
        text = ""
        for line in self.value.splitlines(1):
            text += "!!   %s" % line
        return text

class Message:
    "Message management"
    def __init__(self,verbose=0,logfile="message.log"):
        "Initialisation"
        #List of errors
        self.terrors = list()
        #List of warnings
        self.twarnings = list()
        #Fatal error have been occured or not
        self.tfatal = None
        #Raise an exception (if nofatal==True) for a fatal error instead of exiting
        self.nofatal = False
        #Set of not referenced routines
        self.noreference = dict()
        #Verbosity
        self.verbose = verbose
        #Open the log file
        self.logfile = logfile
        self.log = open(logfile,"w")
    #
    def bug(self,text):
        "Display a message about a possible bug and stops"
        tt = "Bug: %s\n" % text
        sys.stderr.write(tt)
        sys.exit(1)
    #
    def close(self):
        "Close the log flie"
        self.log.close()
    #
    def done(self):
        "Write done"
        self.write("done.\n",verbose=-10,flush=True)
    #
    def error(self,text):
        "Display an error message"
        tt = "Error[%d]: %s\n" % (len(self.terrors)+1,text)
        self.terrors.append(tt)
        if len(self.terrors) == 1:
            sys.stdout.write("\n"+tt)
        else:
            sys.stdout.write(tt)
    #
    def flush(self):
        "Flush the buffer"
        sys.stdout.flush()
    #
    def fatal(self,text):
        "Display an error message and stops"
        sys.stderr.write("\nFatal Error: %s\n" % text)
        sys.stderr.write("With the option --nofatal, abilint will " +\
                "always generate the files of interfaces (already processed).\n")
        self.tfatal = text
        if self.nofatal:
            #Raise an exception
            raise FatalError,text
        else:
            sys.exit(1)
    #
    def final(self):
        "Final statistics: Write in a log file and resume."
        #Write at the end of the log file all warning and errors
        for tt in self.twarnings:
            self.log.write(tt)
        #Not referenced routines
        items = self.noreference.items()
        items.sort()
        for item in items:
            if item == 1:
                self.log.write("The routine %s is not referenced once.\n")
            else:
                self.log.write("The routine %s is not referenced %d times.\n" % item)
        #Errors
        for tt in self.terrors:
            self.log.write(tt)
        if self.tfatal:
            self.log.write(self.tfatal)
            self.log.write("\n" + "*"*20 + " Fatal Error " + "*"*20 + "\n\n")
            sys.stdout.write("\n" + "*"*20 + " Fatal Error " + "*"*20 + "\n\n")
        else:
            self.log.write("%d warnings.\n" % len(self.twarnings))
            self.log.write("%d not referenced routines.\n" % len(self.noreference))
            if self.terrors <= 1:
                self.log.write("%d error.\n" % (len(self.terrors)))
            else:
                self.log.write("%d errors.\n" % (len(self.terrors)))
            sys.stdout.write("%d warnings (%d not referenced routines) -- %d errors.\n" \
                    % (len(self.twarnings),len(self.noreference),len(self.terrors)))
        sys.stdout.write("See the file '%s' for warnings.\n" % self.logfile)
    #
    def section(self,text):
        "Display a message and flush"
        self.write(text,verbose=-10,flush=True)
    #
    def warning(self,text,verbose=0):
        "Display a warning message"
        tt = "Warning[%d]: %s\n" % (len(self.twarnings)+1,text)
        self.twarnings.append(tt)
        if verbose <= self.verbose:
            sys.stdout.write(tt)
    #
    def warning_no_interface(self,dir,file,routine,called):
        "Display a warning message concerning not referenced routine"
        if called in self.noreference.keys():
            self.noreference[called] += 1
        else:
            self.noreference[called] = 1
        self.warning("[%s/%s:%s] No interface for the routine '%s'" \
               % (dir,file,routine,called))
    def warning_no_reference(self,dir,file,routine,called):
        "Display a warning message concerning not referenced routine (for interface)"
        if called in self.noreference.keys():
            self.noreference[called] += 1
        else:
            self.noreference[called] = 1
        self.warning("[%s/%s:%s] No reference for the routine '%s'" \
               % (dir,file,routine,called))
    #
    def write(self,text,verbose=0,flush=False):
        "Display a message"
        if verbose <= self.verbose:
            sys.stdout.write(text)
            #Copy to the log file
            self.log.write(text)
        if flush:
            #Flush stdout
            sys.stdout.flush()


#Header of the interface module.
head_source = \
"""!!****m* ABINIT/%(name)s
!! NAME
!! %(name)s
!!
!! FUNCTION
!! This module contains the interfaces of the routines
!! %(description)s
!!
!! COPYRIGHT
!! Copyright (C) 2005 ABINIT group
!! This file is distributed under the terms of the
!! GNU General Public License, see ~abinit/COPYING
!! or http://www.gnu.org/copyleft/gpl.txt .
!!
!! NOTES
!! THIS FILE IS GENERATED AUTOMATICALLY BY abilint.
!! To do that: abilint . .
!!             (see util/maintainers/abilint.py)
%(warning)s!!
!! SOURCE

module %(name)s

"""


#Special modification inside the code
special_modif =  {}


#Start the script
if __name__ == "__main__":
    #Check arguments
    only = False
    try:
        optlist, args = getopt.getopt(sys.argv[1:],\
            "bg:hlino:v",["beautify","graph=","help","libraries","lint","nofatal","only=","verbose"])
    except getopt.error:
        sys.stderr.write("Error in arguments\n")
        usage(error=1)
    #Help message
    verbose = -10
    beautify = False
    graph = False
    libraries = False
    lint = False
    nofatal = False
    for opt,arg in optlist:
        if   opt == "-b" or opt == "--beautify":
            beautify = True
            #Complete analysis
            lint = True
        elif opt == "-g" or opt == "--graph":
            graph = True
            graph_arg = arg
        elif opt == "-h" or opt == "--help":
            usage(error=0)
        elif opt == "-l" or opt == "--libraries":
            libraries = True
        elif opt == "-i" or opt == "--lint":
            lint = True
        elif opt == "-n" or opt == "--nofatal":
            nofatal = True
        elif opt == "o" or opt == "--only":
            only = True
            dir_only = arg
        elif opt == "-v" or opt == "--verbose":
            verbose = 10
    #Two arguments are required
    if len(args) != 2:
        sys.stderr.write('You have to specify the ABINIT source directory and the destination.\n')
        sys.stderr.write('    Ex: abilint . .\n')
        usage(error=1)
    #Define OLD and NEW directories
    OLD = args[0]
    NEW = args[1]
    if only:
        #Only one directory
        #Create the project and read all files inside src/defs
        abinit = Project(OLD,name="ABINIT",pattern_dir="defs",pattern_file=".*[.]F90$",logfile="abilint.log")
        #Add dir_only directory
        if "src" in dir_only:
            abinit.add("%s/%s" % (OLD,dir_only),pattern_file=".*[.]F90$")
        elif "lib" in dir_only:
            #abinit.add("%s/%s" % (OLD,dir_only),pattern_file="^_.*_$",File_Class=File_F90)
            abinit.add("%s/%s" % (OLD,dir_only),pattern_file=".*[.]F90$",read_only=True)
            abinit.add("%s/%s" % (OLD,dir_only),pattern_file=".*[.](f|F)$",\
                    read_only=True,File_Class=File_F77)
        else:
            sys.stderr.write('The option --only waits for "src/xxx" of "lib/xxx"\n')
            usage(error=1)
        abinit.message.write("Build the interface for the directory '%s'\n" % dir_only,verbose=-10)
        #Raise an exception for a fatal error
        abinit.message.nofatal = nofatal
        try:
            #Analyze the project.
            abinit.analyze_all(except_pattern="interfaces_")
            #Special modifications (to be done once).
            abinit.special(special_modif)
            #Create a file for all the interface and put it in "defs".
            abinit.interfaces_all()
        except FatalError, value:
            #Rescue routine: Create all interfaces
            abinit.remove_children()
            abinit.interfaces_all(warning="!!\n!! WARNING\n" \
                    + "!! No interface generated by abilint\n" \
                    + "!! Please, correct the problem in the code:\n" \
                    + str(value))
    else:
        #Create the project and read all files
        abinit = Project(OLD,name="ABINIT",pattern_file=".*[.]F90$",logfile="abilint.log")
        #Add include files
        abinit.add("%s/src" % OLD,pattern_file=".*[.]inc$",File_Class=File)
        #Add the description file of each library
        abinit.message.write("Add the libraries...",verbose=-10)
        if libraries:
            abinit.add("%s/lib" % OLD,pattern_file=".*[.]F90$",read_only=True)
            abinit.add("%s/lib" % OLD,pattern_file=".*[.](f|F)$",read_only=True,File_Class=File_F77)
            #Add _xxx_ files
            abinit.add("%s/lib" % OLD,pattern_file="^_.*_$",File_Class=File_F90)
            #Remove code except the first comments
            abinit.remove_code("^_.*_$")
        else:
            #Add description files of the libraries
            abinit.add("%s/lib" % OLD,pattern_file="^_.*_$",File_Class=File_F90)
        abinit.message.write("(%d directories, %d files)" \
                % (len(abinit.dirs),len(abinit.files)),verbose=-10)
        abinit.message.done()
        #Raise an exception for a fatal error
        abinit.message.nofatal = nofatal
        try:
            #We check for a cache file
            if NEW == OLD:
                abinit.cache_load(NEW)
            #Analyze the project.
            abinit.analyze_all(except_pattern="interfaces_")
            #Set the called routines
            abinit.set_children_parents()
            if lint:
                #Analyse the interdependencies between directories
                abinit.analyze_directories()
                #Analyze all the comments in order to detect the robodoc structure
                abinit.analyze_comments()
                #Analyse the code (body)
                abinit.analyze_body()
            if NEW == OLD:
                #Backup before changing to optimize the minimal number of copies
                abinit.backup()
            #Create files for all the interfaces and put them in "defs".
            abinit.interfaces_all()
            if libraries:
                #Regenerate the file _xxx_
                abinit.generate("%s/lib" % OLD)
            #Add "use interface_", remove the declarations of functions and add declarations
            abinit.add_use_interface()
            #Special modifications (to be done once).
            abinit.special(special_modif)
        except FatalError, value:
            #Rescue routine: Create all interfaces
            abinit.remove_children()
            abinit.interfaces_all(warning="!!\n!! WARNING\n" \
                    + "!! No interface generated by abilint\n" \
                    + "!! Please, correct the following problem in the code:\n" \
                    + str(value))
    if beautify:
        #Improve the appearance of the code
        abinit.beautify()
    if graph:
        #Build in the file abinit.dot some graph
        abinit.graph(graph_arg)
    #We copy everything        
    abinit.copy_all(NEW,only_if_changed=(NEW == OLD))
    #We copy a cache file
    abinit.cache_save(NEW)
    #Display some statistics
    abinit.statistics()
    #Close the log file
    abinit.message.close()


Generated by  Doxygen 1.6.0   Back to index