# -*- coding: iso-8859-1 -*-

# ==============================================================================
# COPYRIGHT (C) 1991 - 2003  EDF R&D                  WWW.CODE-ASTER.ORG
# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
# (AT YOUR OPTION) ANY LATER VERSION.
#
# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
#
# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
#    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
# ==============================================================================

"""ASTER_BUILD class.
"""

__revision__ = "$Id: as_build.py 2566 2006-12-08 10:09:20Z courtois $"


# ----- differ messages translation
def _(mesg):
   return mesg

import os
import sys
import glob
import re
import copy
import time
import py_compile
from types import ListType, TupleType

fmt_catapy = 'cata%s.py%s'
fcapy  = fmt_catapy % ('', '')
fcapy_ = fmt_catapy % ('', '*')

#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
class ASTER_BUILD:
   """This class provides functions to build a version of Code_Aster.
   """
   repcatapy = ('entete', 'commun', 'commande')
   repcatalo = ('compelem', 'typelem', 'options')

#-------------------------------------------------------------------------------
   def __init__(self, run, conf):
      """run   : ASTER_RUN object
      conf : ASTER_CONFIG object
      """
      # initialisations
      self.verbose = False
      self.debug = False
      # ----- reference to ASTER_RUN object which manages the execution
      self.run = run
      if run != None and run.__class__.__name__ == 'ASTER_RUN':
         self.verbose = run['verbose']
         self.debug = run['debug']
      # ----- Check if 'conf' is a valid ASTER_CONFIG object
      self.conf = conf
      if conf == None or conf.__class__.__name__ != 'ASTER_CONFIG':
         self._mess(_('no configuration object provided !'), _('<F>_ERROR'))
      else:
         # ----- check that ASTER_CONFIG fields are correct (not nul !)
         pass

#-------------------------------------------------------------------------------
   def _mess(self, msg, cod='', store=False):
      """Just print a message
      """
      if hasattr(self.run, 'Mess'):
         self.run.Mess(msg, cod, store)
      else:
         print '%-18s %s' % (cod, msg)

#-------------------------------------------------------------------------------
   def Compil(self, typ, rep, repobj, dbg, rep_trav='', error_if_empty=False):
      """Compile 'rep/*.suffix' files and put '.o' files in 'repobj'
         dbg : debug or nodebug
         (rep can also be a file)
      rep can be remote.
      """
      self.run.DBG('type : %s' % typ, 'rep : %s' % rep, 'repobj : %s' % repobj)
      prev = os.getcwd()
      # ----- get command line from conf object
      if typ == 'C':
         suffix = '.c'
         if self.conf['CC'][0] == '':
            self._mess(_("C compiler not defined in 'config.txt' (CC)"), \
                  _('<F>_CMD_NOT_FOUND'))
         cmd = self.conf['CC'][:]
         if dbg == 'debug':
            cmd.extend(self.conf['OPTC_D'])
         else:
            cmd.extend(self.conf['OPTC_O'])
         cmd.append('-I.')
         cmd.extend(self.conf['INCL'])
      elif typ == 'F':
         suffix = '.f'
         if self.conf['F77'][0] == '':
            self._mess(_("Fortran 77 compiler not defined in 'config.txt' (F77)"), \
                  _('<F>_CMD_NOT_FOUND'))
         cmd = self.conf['F77'][:]
         if dbg == 'debug':
            cmd.extend(self.conf['OPTF_D'])
         else:
            cmd.extend(self.conf['OPTF_O'])
         cmd.append('-I.')
         cmd.extend(self.conf['INCLF'])
      elif typ == 'F90':
         suffix = '.f'
         if self.conf['F90'][0] == '':
            self._mess(_("Fortran 90 compiler not defined in 'config.txt' (F90)"), \
                  _('<F>_CMD_NOT_FOUND'))
         cmd = self.conf['F90'][:]
         if dbg == 'debug':
            cmd.extend(self.conf['OPTF90_D'])
         else:
            cmd.extend(self.conf['OPTF90_O'])
         cmd.append('-I.')
         cmd.extend(self.conf['INCLF90'])
      else:
         self._mess(_('unknown type : %s') % typ, _('<F>_PROGRAM_ERROR'))
      cmd = ' '.join(cmd)
      
      # ----- if force is True, don't use ctime to skip a file
      force = self.run['force']
      
      # ----- copy source files if remote
      if self.run.IsRemote(rep):
         if rep_trav == '':
            self._mess(_('rep_trav must be defined'), _('<F>_PROGRAM_ERROR'))
         obj = os.path.join(rep_trav, '__tmp__'+os.path.basename(rep))
         iret = self.run.MkDir(obj)
         if self.run.IsDir(rep):
            src = [os.path.join(rep, '*'+suffix), os.path.join(rep, '*.h')]
         else:
            src = [rep,]
         iret = self.run.Copy(obj, niverr='SILENT', *src)
         rep = obj
      else:
         rep = self.run.PathOnly(rep)

      # ----- check that directories exist
      if not os.path.isdir(repobj):
         iret = self.run.MkDir(repobj, verbose=True)
      
      # ----- work in repobj
      os.chdir(repobj)

      # ----- log file
      msg = os.path.basename(rep)+'.msg'
      if os.path.exists(msg):
         os.remove(msg)
      fmsg = open(msg, 'w')
      fmsg.write(os.linesep + \
                 'Messages de compilation' + os.linesep + \
                 '=======================' + os.linesep)

      # ----- list of source files
      if self.run.IsDir(rep):
         files = glob.glob(os.path.join(rep, '*'+suffix))
      else:
         files = [rep, ]
      if len(files) == 0:
         iret = 0
         niverr = ''
         if error_if_empty:
            iret = 2
            niverr = _('<E>_NO_SOURCE_FILE')
         self._mess(_('no source file in %s') % rep, niverr)
         return iret, []
      iret = 0
      nskip = 0
      for f in files:
         tail = os.path.join('...', os.path.basename(os.path.dirname(f)),
                                  os.path.basename(f))
         obj = os.path.basename(f)[:-2]+'.o'

         # ----- skip file if .o more recent than source file
         if not force and os.path.exists(obj) \
               and os.stat(obj).st_ctime > os.stat(f).st_ctime:
            nskip += 1
            if self.verbose or self.debug:
               self.run.VerbStart(_('compiling %s') % tail, verbose=True)
               self.run.VerbIgnore(verbose=True)
         else:
            self.run.VerbStart(_('compiling %s') % tail, verbose=True)
            if self.verbose:
               print
            jret, out = self.run.Shell(cmd+' '+f)
            # ----- avoid to duplicate output
            if not self.verbose:
               self.run.VerbEnd(jret, output=out, verbose=True)
            if jret != 0:
               fmsg.write(out)
               self._mess((_('error during compiling %s (see %s)') % \
                     (f, os.path.abspath(msg))),
                     _('<E>_COMPIL_ERROR'), store=True)
            iret = max(iret, jret)
      self._mess(_('%4d files to compile in %s') % (len(files), rep))
      if nskip > 0:
         self._mess(_('%4d files skipped (object more recent than source)') \
                % nskip)

      fmsg.close()
      os.chdir(prev)
      return iret, files

#-------------------------------------------------------------------------------
   def CompilAster(self, REPREF, repdest='', dbg='nodebug', ignore_ferm=False):
      """Compile all Aster source files, put '.o' files in 'repdest'/DIR
      and DIR_f with DIR=obj or dbg depends on 'dbg'
      (DIR for "real" source files and DIR_f for fermetur).
      """
      self._mess(_('Compilation of source files'), 'TITLE')
      dest_o = {  'nodebug' : self.conf['BINOBJ_NODBG'][0],
                  'debug'   : self.conf['BINOBJ_DBG'][0]    }
      dest_f_o = {'nodebug' : self.conf['BINOBJF_NODBG'][0],
                  'debug'   : self.conf['BINOBJF_DBG'][0]   }

      # ----- define type and destination of '*.o' files for each directory
      if repdest == '':
         repdest = REPREF
      dest = os.path.join(repdest, dest_o[dbg])
      dest_f = os.path.join(repdest, dest_f_o[dbg])
      para = {
         self.conf['SRCC'][0]     : ['C', dest],
         self.conf['SRCFOR'][0]   : ['F', dest],
         self.conf['SRCF90'][0]   : ['F90', dest],
      }
      if not ignore_ferm:
         para[self.conf['SRCFERM'][0]] = ['F', dest_f]

      # ----- check that directories exist
      for obj in [os.path.join(REPREF, rep) for rep in para.keys()]:
         if not os.path.exists(obj):
            self._mess(_('directory does not exist : %s') % obj,
                  _('<E>_FILE_NOT_FOUND'))
      self.run.CheckOK()

      # ----- nobuild list
      nobuild = []
      for d in self.conf['NOBUILD']:
         nobuild.extend([os.path.join(REPREF, d) for d in d.split()])
      # bibf90 dirs automatically added if there is no F90 compiler
      if self.conf['F90'][0] == '' and self.conf['SRCF90'][0] != '':
         nobuild.extend([d for d in \
            glob.glob(os.path.join(REPREF, self.conf['SRCF90'][0], '*'))
            if not d in nobuild])
         self._mess(_("There is no Fortran 90 compiler in 'config.txt'. " \
               "All source files from '%s' were ignored.") % \
               self.conf['SRCF90'][0], _('<A>_IGNORE_F90'), store=True)
      if len(nobuild)>0:
         self._mess(_('These directories will not be built :'))
         for d in nobuild:
            print ' '*10+'>>> %s' % d
      
      # ----- compilation
      iret = 0
      for module in [d for d in para.keys() if d != '']:
         if module != self.conf['SRCFERM'][0]:
            # ----- glob subdirectories
            lrep = glob.glob(os.path.join(REPREF, module, '*'))
         else:
            lrep = (os.path.join(REPREF, module), )
         for rep in lrep:
            if os.path.isdir(rep):
               tail = '...'+re.sub('^'+REPREF, '', rep)
               print
               jret = 0
               if not rep in nobuild:
                  self._mess(_('Compilation from %s directory') % tail)
                  jret, bid = self.Compil(para[module][0], rep, para[module][1], dbg)
               else:
                  self.run.VerbStart(_('Directory %s is in NOBUILD list') % tail, \
                        verbose=True)
                  self.run.VerbIgnore(verbose=True)
               iret = max(iret, jret)

      return iret

#-------------------------------------------------------------------------------
   def Archive(self, repobj, lib):
      """Archive all .o files from 'repobj' into 'lib'
      All args must be local.
      """
      self._mess(_('Archive object files'), 'TITLE')
      prev = os.getcwd()
      # ----- get command line from conf object
      cmd0 = [self.conf['LIB'][0]+' '+lib]

      # ----- check that directories exist
      if not os.path.isdir(repobj):
         self._mess(_('directory does not exist : %s') % repobj,
               _('<E>_FILE_NOT_FOUND'))
      rep = os.path.dirname(lib)
      if not os.path.isdir(rep):
         iret = self.run.MkDir(rep, verbose=True)
      self.run.CheckOK()

      # ----- if force is True, don't use ctime to skip a file
      force = self.run['force']
      if not os.path.exists(lib):
         force = True
         tlib = -1
      else:
         tlib = os.stat(lib).st_ctime

      # ----- work in repobj
      os.chdir(repobj)

      # ----- list of source files
      files = glob.glob('*.o')
      ntot = len(files)
      if ntot == 0:
         self._mess(_('no object file in %s') % rep)
         return 0
      
      # ----- sleep 1 s to be sure than last .o will be older than lib !
      time.sleep(1)

      # ----- use MaxCmdLen to limit command length
      iret = 0
      nskip = 0
      while len(files) > 0:
         cmd = copy.copy(cmd0)
         clen = len(cmd[0])+1
         nadd = 0
         while len(files) > 0 and clen+len(files[0]) < self.run.system.MaxCmdLen-15:
            if not force and tlib > os.stat(files[0]).st_ctime:
               nskip += 1
               bid = files.pop(0)
            else:
               clen = clen+len(files[0])+1
               cmd.append(files.pop(0))
               nadd += 1
         self.run.VerbStart(_('%4d / %4d objects added to archive...') % \
               (nadd, ntot), verbose=True)
         if nadd > 0:
            if self.verbose:
               print
            jret, out = self.run.Shell(' '.join(cmd))
            # ----- avoid to duplicate output
            if not self.verbose:
               self.run.VerbEnd(jret, output=out, verbose=True)
            if jret != 0:
               self._mess(_('error during archiving'), _('<E>_ARCHIVE_ERROR'))
            iret = max(iret, jret)
         else:
            self.run.VerbIgnore(verbose=True)

      self._mess(_('%4d files archived') % ntot)
      if nskip > 0:
         self._mess(_('%4d files skipped (objects older than library)') \
                % nskip)
      os.chdir(prev)
      return iret

#-------------------------------------------------------------------------------
   def Link(self, exe, lobj, libaster, libferm, reptrav):
      """Link a number of object and archive files into an executable.
         exe      : name of the executable to build
         lobj     : list of object files
         libaster : Code_Aster main library
         libferm  : Code_Aster fermetur library
         reptrav  : working directory
      If "python.o" is not in 'lobj' it will be extracted from 'libaster'.
      Other libs are given by ASTER_CONFIG object.
      None argument must not be remote !
      """
      self._mess(_('Build Code_Aster executable'), 'TITLE')
      prev = os.getcwd()
      # ----- check that directories exist
      for obj in lobj+[libaster, libferm]:
         if not os.path.exists(obj):
            self._mess(_('file not found : %s') % obj, _('<E>_FILE_NOT_FOUND'))
      rep = os.path.dirname(exe)
      if not os.path.isdir(rep):
         iret = self.run.MkDir(rep, verbose=True)
      self.run.CheckOK()

      # ----- if force is True, don't use ctime to skip a file
      force = self.run['force']
      if not os.path.exists(exe):
         force = True
      else:
         texe = os.stat(exe).st_ctime
         for f in lobj+[libaster, libferm]:
            if os.stat(f).st_ctime > texe:
               force = True
               self.run.DBG('%s more recent than %s' % (f, exe))
               break

      # ----- work in reptrav
      os.chdir(reptrav)

      # ----- python.o in lobj ?
      mainobj = self.conf['BINOBJ_MAIN'][0]
      if mainobj not in [os.path.basename(o) for o in lobj] and force:
         # get 'ar' command without arguments, if it's not 'ar' &$£#@§!
         cmd = [self.conf['LIB'][0].split()[0]]
         cmd.extend(['-xv', libaster, mainobj])
         cmd = ' '.join(cmd)
         self.run.VerbStart(_('extracting %s from %s...') % \
               (repr(mainobj), os.path.basename(libaster)), verbose=True)
         if self.verbose:
            print
         iret, out = self.run.Shell(cmd)
         # ----- avoid to duplicate output
         if not self.verbose:
            self.run.VerbEnd(iret, output=out, verbose=True)
         if iret != 0:
            self._mess(_('error during extracting %s') % repr(mainobj),
                  _('<F>_LIBRARY_ERROR'))
         lobj.insert(0, mainobj)
      else:
         lobj = [o for o in lobj if os.path.basename(o) == mainobj] +\
                [o for o in lobj if os.path.basename(o) != mainobj]

      # ----- get command line from conf object and args
      cmd = copy.copy(self.conf['LINK'])
      cmd.extend(self.conf['OPTL'])
      cmd.append('-o')
      cmd.append(exe)
      cmd.extend(lobj)
      cmd.append(libaster)
      cmd.extend(self.conf['BIBL'])
      cmd.append(libferm)
      cmd = ' '.join(cmd)

      # ----- run ld or skip this stage
      iret = 0
      tail = os.path.join('...', os.path.basename(rep), os.path.basename(exe))
      self.run.VerbStart(_('creating %s...') % tail, verbose=True)
      if force:
         if self.verbose:
            print
         iret, out = self.run.Shell(cmd)
         # ----- avoid to duplicate output
         if not self.verbose:
            self.run.VerbEnd(iret, output=out, verbose=True)
         if iret != 0:
            self._mess(_('error during linking'), _('<E>_LINK_ERROR'))
      else:
         self.run.VerbIgnore(verbose=True)
         self._mess(_('executable more recent than objects and libs ' \
               'in arguments'))

      os.chdir(prev)
      return iret

#-------------------------------------------------------------------------------
   def PrepEnv(self, REPREF, repdest, dbg='nodebug', lang='', **kargs):
      """Prepare 'repdest' with Code_Aster environment from REPREF and
      given arguments in kargs[k] (k in 'exe', 'cmde', 'ele', 'py', 'unigest').
      Note : - REPREF/elements and kargs['ele'] don't exist when building
         elements for the first time.
             - only PYSUPPR entries from 'unigest' are considered here.
      All elements of kargs can be remote.
      """      
      reptrav = os.path.join(repdest, self.conf['REPPY'][0], '__tmp__')
      prev = os.getcwd()
      # ----- check that files and directories in kargs exist
      for occ in kargs.values():
         obj = occ
         if not type(occ) in (ListType, TupleType):
            obj = [occ]
         for rep in obj:
            if not self.run.Exists(rep):
               self._mess(_('path does not exist : %s') % rep,
                     _('<E>_FILE_NOT_FOUND'))
      if not os.path.isdir(repdest):
         iret = self.run.MkDir(repdest, verbose=True)
      self.run.CheckOK()

      # ----- language
      if lang in ('', 'fr'):
         lang = 'fr'
         suff = ''
      else:
         suff = '_' + lang
      mask_catapy = fmt_catapy % (suff, '*')
      
      # ----- copy REPREF/aster?.exe or kargs['exe']
      iret = 0
      if kargs.has_key('exe'):
         src = kargs['exe']
      else:
         if dbg == 'debug':
            src = os.path.join(REPREF, self.conf['BIN_DBG'][0])
         else:
            src = os.path.join(REPREF, self.conf['BIN_NODBG'][0])
      dest = repdest
      if src != os.path.join(repdest, os.path.basename(src)):
         if not self.run.IsRemote(src) and self.run['symlink'] != False:
            src = self.run.PathOnly(src)
            self.run.VerbStart(_('adding a symbolic link to %s...') % src, verbose=True)
            iret = 0
            output = ''
            try:
               lien = os.path.join(repdest, os.path.basename(src))
               if os.path.exists(lien):
                  self.run.Delete(lien)
               os.symlink(src, lien)
            except OSError, output:
               iret = 4
               self._mess(_('error occurs during creating a symbolic link' \
                           ' from %s to %s') % (src, dest), _('<E>_SYMLINK'))
            self.run.VerbEnd(iret, output, verbose=True)
         else:
            iret = self.run.Copy(dest, src, verbose=True)
      else:
         self.run.VerbStart(_('copying %s...') % src, verbose=True)
         self.run.VerbIgnore(verbose=True)

      # ----- copy REPREF/bibpyt
      self.run.MkDir(os.path.join(repdest, self.conf['REPPY'][0], 'Cata'))
      iret = 0
      src  = os.path.join(REPREF, self.conf['SRCPY'][0], '*')
      dest = os.path.join(repdest, self.conf['REPPY'][0])
      iret = self.run.Copy(dest, src)

      # if 'py' is in kargs
      if kargs.has_key('py'):
         self._mess(_("copy user's python source files"))
         # ----- python source given by user
         lobj = kargs['py']
         if not type(lobj) in (ListType, TupleType):
            lobj = [kargs['py'], ]
         for occ in lobj:
            obj = occ
            if self.run.IsRemote(occ):
               obj = reptrav
               iret = self.run.MkDir(obj)
               if self.run.IsDir(occ):
                  src = os.path.join(occ, '*.py')
               else:
                  src = occ
               iret = self.run.Copy(obj, src, verbose=True)
               files = glob.glob(os.path.join(obj, '*.py'))
            else:
               occ = self.run.PathOnly(occ)
               if self.run.IsDir(occ):
                  files = glob.glob(os.path.join(occ, '*.py'))
               else:
                  files = [occ, ]
            for f in files:
               mod, rep = self.GetCModif('py', f)
               dest = os.path.join(repdest, self.conf['REPPY'][0], rep, mod+'.py')
               iret = self.run.Copy(dest, f, verbose=True)

      # ----- apply PYSUPPR directives from unigest
      if kargs.has_key('unigest'):
         for f in kargs['unigest']['py']:
            self.run.Delete(os.path.join(repdest, \
                  re.sub('^'+self.conf['SRCPY'][0], self.conf['REPPY'][0], f)))

      # ----- copy REPREF/commande or kargs['cmde']
      iret = 0
      if kargs.has_key('cmde'):
         src = kargs['cmde']
      else:
         src = os.path.join(REPREF, self.conf['BINCMDE'][0])
      dest = os.path.join(repdest, self.conf['REPPY'][0], 'Cata')
      if not self.run.IsRemote(src) and self.run['symlink'] != False:
         src = self.run.PathOnly(src)
         # checks catalogue exists
         l_capy = glob.glob(os.path.join(src, mask_catapy))
         if len(l_capy) == 0 and lang != '':
            self._mess(_("no catalogue found for language '%s', " \
                         "use standard (fr) one...") % lang)
            mask_catapy = fmt_catapy % ('', '*')
            l_capy = glob.glob(os.path.join(src, mask_catapy))
         if len(l_capy) == 0:
            self._mess(_('no catalogue found : %s') % os.path.join(src, mask_catapy),
                  _('<F>_FILE_NOT_FOUND'))
         # add symlink
         for f in l_capy:
            root = os.path.splitext(fcapy)[0]
            ext  = os.path.splitext(f)[-1]
            lien = os.path.join(dest, root + ext)
            self.run.VerbStart(_('adding a symbolic link %s to %s...') % \
                                (os.path.basename(lien), f), verbose=True)
            iret = 0
            output = ''
            try:
               if os.path.exists(lien):
                  self.run.Delete(lien)
               os.symlink(f, lien)
            except OSError, output:
               iret = 4
               self._mess(_('error occurs during creating a symbolic link' \
                            ' from %s to %s') % (src, dest), _('<E>_SYMLINK'))
            self.run.VerbEnd(iret, output, verbose=True)
      else:
         iret = self.run.Copy(dest, os.path.join(src, mask_catapy), verbose=True)
         for f in glob.glob(os.path.join(dest, mask_catapy)):
            root = os.path.splitext(fcapy)[0]
            ext  = os.path.splitext(f)[-1]
            os.rename(f, os.path.join(dest, root + ext))

      # ----- copy REPREF/elements or kargs['ele']
      iret = 0
      if kargs.has_key('ele'):
         src = kargs['ele']
      else:
         src = os.path.join(REPREF, self.conf['BINELE'][0])
      if self.run.Exists(src) and src != os.path.join(repdest, 'elem.1'):
         # symlink not allowed for elem.1 (rw)
         iret = self.run.Copy(os.path.join(repdest, 'elem.1'), src, verbose=True)
      else:
         self.run.VerbStart(_('copying %s...') % src, verbose=True)
         self.run.VerbIgnore(verbose=True)

      # ----- result directories
      os.chdir(repdest)
      self.run.MkDir('RESU_ENSIGHT')
      self.run.MkDir('REPE_OUT')

      os.chdir(prev)
      self.run.Delete(reptrav)
      return iret

#-------------------------------------------------------------------------------
   def CompilCapy(self, REPREF, reptrav, exe='', cmde='', i18n=False, **kargs):
      """Compile commands catalogue from REPREF/commande using 'exe'
      and puts result in 'cmde'. kargs can contain :
         capy    : list of user's capy files
         unigest : GetUnigest dict with "CATSUPPR catapy module" entries
      All args can be remote.
      If `i18n` is True, translates cata.py.
      """
      self._mess(_('Compilation of commands catalogue'), 'TITLE')
      prev = os.getcwd()
      if exe == '':
         exe = os.path.join(REPREF, self.conf['BIN_NODBG'][0])
      if cmde == '':
         cmde = os.path.join(REPREF, self.conf['BINCMDE'][0])
      # ----- check that files and directories in kargs exist
      for occ in kargs.values():
         obj = occ
         if not type(occ) in (ListType, TupleType):
            obj = [occ]
         for rep in obj:
            if not self.run.Exists(rep):
               self._mess(_('path does not exist : %s') % rep,
                     _('<E>_FILE_NOT_FOUND'))
      if not os.path.isdir(cmde):
         iret = self.run.MkDir(cmde, verbose=True)
      if not os.path.isdir(reptrav):
         iret = self.run.MkDir(reptrav, verbose=True)
      self.run.CheckOK()

      # ----- work in reptrav
      os.chdir(reptrav)

      # ----- if force is True, don't use ctime to skip a file
      force = self.run['force']

      # ----- copy capy from REPREF
      if os.path.exists(self.conf['SRCCAPY'][0]):
         self.run.Delete(self.conf['SRCCAPY'][0])
      iret = self.run.Copy(self.conf['SRCCAPY'][0],
            os.path.join(REPREF, self.conf['SRCCAPY'][0]), verbose=True)

      # if 'capy' is in kargs
      if kargs.has_key('capy'):
         force = True
         self._mess(_("copy user's catalogues"))
         # ----- catapy given by user
         lobj = kargs['capy']
         if not type(obj) in (ListType, TupleType):
            lobj = [kargs['capy'], ]
         for occ in lobj:
            obj = occ
            if self.run.IsRemote(occ):
               obj = os.path.join(self.conf['SRCCAPY'][0],
                                  '__tmp__'+os.path.basename(occ))
               iret = self.run.MkDir(obj)
               if self.run.IsDir(occ):
                  src = os.path.join(occ, '*.capy')
               else:
                  src = occ
               iret = self.run.Copy(obj, src)
               files = glob.glob(os.path.join(obj, '*.capy'))
            else:
               occ = self.run.PathOnly(occ)
               if self.run.IsDir(occ):
                  files = glob.glob(os.path.join(occ, '*.capy'))
               else:
                  files = [occ, ]
            for f in files:
               mod, rep = self.GetCModif('capy', f)
               dest = os.path.join(self.conf['SRCCAPY'][0], rep)
               iret = self.run.Copy(dest, f, verbose=True)
               if not rep in self.repcatapy:
                  self._mess(_('unknown module name : %s') % rep, _('<A>_ALARM'))

      # ----- apply CATSUPPR directives from unigest
      if kargs.has_key('unigest'):
         force = True
         for f in kargs['unigest']['capy']:
            self.run.Delete(os.path.join(reptrav, f))

      # ----- build cata.py
      if os.path.exists(fcapy):
         self.run.Delete(fcapy)

      # ----- test if compilation can be skipped
      if force or not os.path.exists(os.path.join(cmde, fcapy)):
         force = True
      else:
         tcpy = os.stat(os.path.join(cmde, fcapy)).st_ctime
         ltest = glob.glob(os.path.join(self.conf['SRCCAPY'][0], '*', '*.capy'))
         for f in ltest:
            if os.stat(f).st_ctime >= tcpy:
               force = True
               break

      if not force:
         self.run.VerbStart(_('compilation of commands'), verbose=True)
         self.run.VerbIgnore(verbose=True)
      else:

         fo = open(fcapy, 'w')
         for rep in self.repcatapy:
            for capy in glob.glob(os.path.join(self.conf['SRCCAPY'][0], rep, '*.capy')):
               fo2 = open(capy, 'r')
               fo.write(fo2.read())
               fo2.close()
         fo.close()

         # ----- compile cata.py
         dtmp = kargs.copy()
         dtmp['exe'] = exe
         dtmp['cmde'] = reptrav
         if kargs.has_key('py'):
            dtmp['py'] = kargs['py']
         self.PrepEnv(REPREF, reptrav, **dtmp)
         
         self.run.VerbStart(_('compilation of commands'), verbose=True)
         cmd_import = """
import sys
sys.path.insert(0, "%s")
iret = 0
try:
   from Cata import cata
   from Cata.cata import JdC
   cr = JdC.report()
   if not cr.estvide() :
      iret = 4
      print ">> Catalogue de commandes : DEBUT RAPPORT"
      print cr
      print ">> Catalogue de commandes : FIN RAPPORT"
except:
   iret = 4
   import traceback
   traceback.print_exc(file=sys.stdout)
sys.exit(iret)
""" % self.conf['REPPY'][0]
         cmd_import_file = os.path.join(reptrav, 'cmd_import.py')
         open(cmd_import_file, 'w').write(cmd_import)
         
         if self.verbose:
            print
         cmd = [os.path.join('.', os.path.basename(exe))]
         cmd.append(cmd_import_file)
         iret, out = self.run.Shell(' '.join(cmd))
         # ----- avoid to duplicate output
         if not self.verbose:
            self.run.VerbEnd(iret, output=out, verbose=True)
         
         if iret != 0:
            self._mess(_('error during compiling %s') % fcapy,
                  _('<E>_CATAPY_ERROR'))
         else:
            kret = self.run.Copy(cmde,
                   os.path.join(self.conf['REPPY'][0], 'Cata', fcapy_), verbose=True)
            self._mess(_('Commands catalogue successfully compiled'), 'OK')

         
         # --- translations...
         if i18n:
            pattern = '#:LANG:%s:LANG_END:'
            old     = pattern % '.*'
            exp     = re.compile(old, re.MULTILINE | re.DOTALL)
            for lang in [l for l in self.conf['I18N'] if not l in ('', 'fr')]:
               self.run.VerbStart(_('working for i18n (language %s)...') % lang, verbose=True)
               print
               new  = pattern % (os.linesep + "lang = '%s'" % lang + os.linesep + '#')
               cata_out  = fmt_catapy % ('_' + lang, '')
               cata_out_ = fmt_catapy % ('_' + lang, '*')
               tmpl_dict = os.path.join(self.run['rep_trav'],
                                        'template_dict_%s.py' % lang)
               # fake aster module
               open(os.path.join(self.conf['REPPY'][0], 'aster.py'), 'w').write("""
# fake aster module
""")
               sys.path.insert(0, self.conf['REPPY'][0])
               iret = 0
               try:
                  from Cata.i18n_cata import i18n_make_cata_fich
                  txt_cata = i18n_make_cata_fich(fichier_dtrans=tmpl_dict, lang=lang)
                  txt_orig = []
                  fo = open(cata_out, 'w')
                  # prendre l'entete
                  for rep in self.repcatapy[:1]:
                     for capy in glob.glob(os.path.join(self.conf['SRCCAPY'][0], rep, '*.capy')):
                        txt0 = open(capy, 'r').read()
                        txt2 = exp.sub(new, txt0)
                        fo.write(txt2)
                  # reprendre les imports
                  for rep in self.repcatapy[1:]:
                     for capy in glob.glob(os.path.join(self.conf['SRCCAPY'][0], rep, '*.capy')):
                        fo.write(get_txt_capy(capy))
                        txt_orig.append(open(capy, 'r').read())
                  fo.write(os.linesep)
                  fo.write(txt_cata)
                  fo.write(os.linesep)
                  fo.write(os.linesep.join(txt_orig))
                  fo.close()
               except:
                  iret = 4
                  import traceback
                  traceback.print_exc(file=sys.stdout)
                        
               # ----- avoid to duplicate output
               if not self.verbose:
                  self.run.VerbEnd(iret, output=out, verbose=True)
               
               if iret != 0:
                  self._mess(_("error during building %s") % cata_out, _('<E>_I18N_ERROR'))
               else:
                  kret = self.run.Copy(cmde, cata_out_, verbose=True)
                  self._mess(_('Commands catalogue successfully translated (language %s)') % lang, 'OK')

      os.chdir(prev)
      self.run.Delete(reptrav)
      return iret

#-------------------------------------------------------------------------------
   def CompilEle(self, REPREF, reptrav, ele='', **kargs):
      """Compile elements catalogue as 'ele' from cata_ele.pickled.
      kargs must contain arguments for a Code_Aster execution (i.e. for PrepEnv)
      and optionnaly :
         cata    : a list of user's elements files
         unigest : GetUnigest dict with "CATSUPPR catalo module" entries
         (make_surch_offi needs unigest filename)
      All args can be remote.
      """
      self._mess(_('Compilation of elements catalogue'), 'TITLE')
      required = ('exe', 'cmde')
      if ele == '':
         ele = os.path.join(REPREF, self.conf['BINELE'][0])
      prev = os.getcwd()
      # ----- check for required arguments
      for a in required:
         if not kargs.has_key(a):
            self._mess('(CompilEle) '+_('argument %s is required') % a,
                  _('<E>_PROGRAM_ERROR'))
      
      # ----- check that files and directories in kargs exist
      for occ in kargs.values():
         obj = occ
         if not type(occ) in (ListType, TupleType):
            obj = [occ]
         for rep in obj:
            if not self.run.Exists(rep):
               self._mess(_('path does not exist : %s') % rep,
                     _('<E>_FILE_NOT_FOUND'))
      if not os.path.isdir(reptrav):
         iret = self.run.MkDir(reptrav, verbose=True)
      self.run.CheckOK()

      # ----- work in repobj
      os.chdir(reptrav)

      # ----- if force is True, don't use ctime to skip a file
      force = self.run['force']

      # 1. ----- if 'cata' is in kargs
      if kargs.has_key('cata'):
         force = True
         self._mess(_("copy user's catalogues"))
         fo = open('surch.cata', 'w')
         # ----- catapy given by user
         lobj = kargs['cata']
         if not type(obj) in (ListType, TupleType):
            lobj = [kargs['cata'], ]
         for occ in lobj:
            obj = occ
            if self.run.IsRemote(occ):
               obj = os.path.join(reptrav, '__tmp__'+os.path.basename(occ))
               iret = self.run.MkDir(obj)
               if self.run.IsDir(occ):
                  src = os.path.join(occ, '*.cata')
               else:
                  src = occ
               iret = self.run.Copy(obj, src)
               files = glob.glob(os.path.join(obj, '*.cata'))
            else:
               occ = self.run.PathOnly(occ)
               if self.run.IsDir(occ):
                  files = glob.glob(os.path.join(occ, '*.cata'))
               else:
                  files = [occ, ]
            for f in files:
               fo2 = open(f, 'r')
               self.run.VerbStart(_('adding %s') % f, verbose=True)
               fo.write(fo2.read())
               self.run.VerbEnd(iret=0, output='', verbose=True)
               fo2.close()
         fo.close()

      # 2. ----- compile elements with MAKE_SURCH_OFFI
      self.PrepEnv(REPREF, reptrav, **kargs)
      iret = 0
      cmd = [os.path.join('.', os.path.basename(kargs['exe']))]
      cmd.append(os.path.join(self.conf['REPPY'][0], self.conf['MAKE_SURCH_OFFI'][0]))
      cmd.extend(self.conf['REPPY'])
      cmd.append('MAKE_SURCH')
      cmd.append('surch.cata')
      funig = 'unigest_bidon'
      if kargs.has_key('unigest'):
         force = True
         funig = kargs['unigest']['filename']
         if self.run.IsRemote(funig):
            funig = 'unigest'
            kret = self.run.Copy(funig, kargs['unigest']['filename'])
         else:
            funig = self.run.PathOnly(funig)
      cmd.append(funig)
      cmd.append(os.path.join(REPREF, self.conf['BINPICKLED'][0]))
      cmd.append('fort.4')
      self.run.VerbStart(_('pre-compilation of elements with %s') % \
            os.path.basename(self.conf['MAKE_SURCH_OFFI'][0]), verbose=True)

      # 2.1. ----- test if compilation can be skipped
      if force or not os.path.exists(ele):
         force = True
      else:
         tele = os.stat(ele).st_ctime
         ltest = [os.path.join(REPREF, self.conf['BINPICKLED'][0]), ]
         ltest.extend(glob.glob(os.path.join(self.conf['REPPY'][0], '*', '*.py')))
         for f in ltest:
            if os.stat(f).st_ctime >= tele:
               force = True
               self.run.DBG('%s more recent than %s' % (f, ele))
               break

      if not force:
         self.run.VerbIgnore(verbose=True)
      else:
         if self.verbose:
            print
         jret, out = self.run.Shell(' '.join(cmd))
         # ----- avoid to duplicate output
         if not self.verbose:
            self.run.VerbEnd(jret, output=out, verbose=True)
         if jret != 0:
            self._mess(_('error during pre-compilation of elements'),
                  _('<F>_CATAELE_ERROR'))

      # 3. ----- build elem.1 with kargs['exe']
      cmd = [os.path.join('.', os.path.basename(kargs['exe']))]
      cmd.append(os.path.join(self.conf['REPPY'][0], self.conf['ARGPYT'][0]))
      cmd.extend(self.conf['ARGEXE'])
      cmd.append('-commandes fort.1 -mem 8 -rep none -tpmax 60')
      fo = open('fort.1', 'w')
      fo.write("""
# COMPILATION DU CATALOGUE D'ELEMENT
DEBUT ( CATALOGUE = _F( FICHIER = 'CATAELEM' , UNITE = 4 ))
MAJ_CATA ( ELEMENT = _F())
FIN()
""")
      fo.close()
      self.run.VerbStart(_('compilation of elements with %s') \
            % kargs['exe'], verbose=True)
      
      if not force:
         self.run.VerbIgnore(verbose=True)
      else:
         if self.verbose:
            print
         jret, out = self.run.Shell(' '.join(cmd))
         # ----- diagnostic of Code_Aster execution
         diag = self.GetDiag()[0]
         if self.run.GetGrav(diag) < self.run.GetGrav('<S>') \
             and os.path.exists('elem.1'):
            jret = 0
         else:
            jret = 4
         # ----- avoid to duplicate output
         if not self.verbose:
            self.run.VerbEnd(jret, output=out, verbose=True)

         if jret == 0:
            self._mess(_('Elements catalogue successfully compiled'), diag)
         else:
            self._mess(_('error during compilation of elements'),
                  _('<F>_CATAELE_ERROR'))

         # 4. ----- copy elements to 'ele'
         iret = self.run.Copy(ele, 'elem.1', verbose=True)

      os.chdir(prev)
      self.run.Delete(reptrav)
      return iret

#-------------------------------------------------------------------------------
   def MakePickled(self, REPREF, reptrav, repdest='', **kargs):
      """Make 'repdest'/cata_ele.pickled.
      kargs can contain :
         exe     : Code_Aster executable or Python interpreter (can be remote)
      """
      self._mess(_('Prepare cata_ele.pickled'), 'TITLE')
      if repdest == '':
         repdest = REPREF
      prev = os.getcwd()
      # ----- check if reptrav exists
      if not os.path.isdir(reptrav):
         iret = self.run.MkDir(reptrav, verbose=True)

      # ----- work in repobj
      os.chdir(reptrav)

      # ----- if force is True, don't use ctime to skip a file
      force = self.run['force']

      # ----- use REPREF/aster?.exe or kargs['exe']
      iret = 0
      if kargs.has_key('exe'):
         exe = kargs['exe']
         if self.run.IsRemote(exe):
            kret = self.run.Copy(reptrav, exe)
            exe = os.path.join(reptrav, os.path.basename(exe))
         else:
            exe = self.run.PathOnly(exe)
      else:
         exe = os.path.join(REPREF, self.conf['BIN_NODBG'][0])
      
      # ----- call MAKE_CAPY_OFFI
      cmd = [exe, ]
      cmd.append(os.path.join(REPREF, self.conf['SRCPY'][0], self.conf['MAKE_CAPY_OFFI'][0]))
      cmd.append(os.path.join(REPREF, self.conf['SRCPY'][0]))
      cmd.append('TRAV_PICKLED')
      cmd.append(os.path.join(REPREF, self.conf['SRCCATA'][0]))
      cata_pickled = os.path.basename(self.conf['BINPICKLED'][0])
      cmd.append(cata_pickled)

      # ----- test if compilation can be skipped
      if not os.path.exists(os.path.join(repdest, self.conf['BINPICKLED'][0])):
         force = True
      else:
         tpick = os.stat(os.path.join(repdest, self.conf['BINPICKLED'][0])).st_ctime
         ltest = glob.glob(os.path.join(REPREF, self.conf['SRCCATA'][0], '*', '*.cata'))
         ltest.extend(glob.glob(os.path.join(REPREF, self.conf['SRCPY'][0], '*', '*.py')))
         for f in ltest:
            if os.stat(f).st_ctime >= tpick:
               force = True
               break

      self.run.VerbStart(_('build cata_ele.pickled with %s') % \
            os.path.basename(self.conf['MAKE_CAPY_OFFI'][0]), verbose=True)
      
      if not force:
         self.run.VerbIgnore(verbose=True)
      else:
         if self.verbose:
            print
         jret, out = self.run.Shell(' '.join(cmd))
         # ----- avoid to duplicate output
         if not self.verbose:
            self.run.VerbEnd(jret, output=out, verbose=True)
         if jret == 0 and os.path.exists(cata_pickled):
            self._mess(_('cata_ele.pickled successfully created'), 'OK')
         else:
            self._mess(_('error during making cata_ele.pickled'),
                  _('<F>_PICKLED_ERROR'))

         # ----- copy cata_ele.pickled to 'repdest'
         iret = self.run.Copy(os.path.join(repdest, self.conf['BINPICKLED'][0]),
                              cata_pickled, verbose=True)

      os.chdir(prev)
      self.run.Delete(reptrav)
      return iret

#-------------------------------------------------------------------------------
   def GetUnigest(self, funig):
      """Build a dict of the file names by parsing unigest file 'funig' where
      the keys are 'f', 'f90', 'c', 'py', 'capy', 'cata', 'test' and 'fdepl'.
      funig can be remote.
      WARNING : 'f90' not yet supported !
      """
      dico = {}
      for cat in ('f', 'f90', 'c', 'py', 'capy', 'cata', 'test', 'fdepl'):
         dico[cat] = []

      # ----- keep a reference to the file for CompilEle
      dico['filename'] = funig

      # ----- check that file exists
      if not self.run.Exists(funig):
         self._mess(_('file not found : %s') % funig, _('<A>_ALARM'))
         return dico

      # ----- copy funig if it's a remote file
      if self.run.IsRemote(funig):
         name = '__tmp__.unigest'
         kret = self.run.Copy(name, funig)
      else:
         name = self.run.PathOnly(funig)

      # parse unigest
      fo = open(name, 'r')
      for line in fo:
         mat = re.search('^ *FORSUPPR +([-_a-zA-Z0-9]+) +([-_a-zA-Z0-9]+)', line)
         if mat:
            nam = mat.group(1).lower()+'.f'
            mod = mat.group(2).lower()
            dico['f'].append(os.path.join(self.conf['SRCFOR'][0], mod, nam))
         mat = re.search('^ *F90SUPPR +([-_a-zA-Z0-9]+) +([-_a-zA-Z0-9]+)', line)
         if mat:
            nam = mat.group(1).lower()+'.f'
            mod = mat.group(2).lower()
            dico['f'].append(os.path.join(self.conf['SRCF90'][0], mod, nam))
         mat = re.search('^ *CSUPPR +([-_a-zA-Z0-9]+) +([-_a-zA-Z0-9]+)',
               line)
         if mat:
            nam = mat.group(1).lower()+'.c'
            mod = mat.group(2).lower()
            dico['c'].append(os.path.join(self.conf['SRCC'][0], mod, nam))
         mat = re.search('^ *PYSUPPR +([-_a-zA-Z0-9]+) +([-_a-zA-Z0-9]+)',
               line)
         if mat:
            nam = mat.group(1)+'.py'
            mod = mat.group(2)
            dico['py'].append(os.path.join(self.conf['SRCPY'][0], mod, nam))
         mat = re.search('^ *CATSUPPR +([-_a-zA-Z0-9]+) +([-_a-zA-Z0-9]+)', line)
         if mat:
            nam = mat.group(1).lower()
            mod = mat.group(2).lower()
            if mod in self.repcatapy:
               dico['capy'].append(os.path.join(self.conf['SRCCAPY'][0], mod, nam+'.capy'))
            elif mod in self.repcatalo:
               dico['cata'].append(os.path.join(self.conf['SRCCATA'][0], mod, nam+'.cata'))
            else:
               self._mess(_('unknown type %s in unigest file') % mod,
                     _('<A>_ALARM'))
         mat = re.search('^ *TESSUPPR +([-_a-zA-Z0-9]+)', line)
         if mat:
            nam = mat.group(1)+'.*'
            dico['test'].append(os.path.join(self.conf['SRCTEST'][0], nam))
         mat = re.search('^ *FORDEPLA +([-_a-zA-Z0-9]+) +([-_a-zA-Z0-9]+)' \
               ' +([-_a-zA-Z0-9]+)', line)
         if mat:
            nam = mat.group(1).lower()+'.f'
            old = mat.group(2).lower()
            new = mat.group(3).lower()
            dico['fdepl'].append([os.path.join(self.conf['SRCFOR'][0], old, nam),
                  os.path.join(self.conf['SRCFOR'][0], new, nam)])
      fo.close()
      return dico

#-------------------------------------------------------------------------------
   def GetDiag(self, err='fort.9', resu='fort.8', mess='fort.6', cas_test=False):
      """Return the diagnostic after a Code_Aster execution
      as a list : [diag, tcpu, tsys, ttotal]
         err   : error file
         resu  : result file (None = ignored)
         mess  : messages file (None = ignored)
      """
      diag, tcpu, tsys, ttot = _('<F>_ABNORMAL_ABORT'), 0., 0., 0.
      
      # 1. ----- parse error file
      diag2, stop = _diag_erre(err)
      diag = diag2 or diag
      if stop:
         self.run.ASSERT(diag != None)
      else:
         # if .erre doesn't exist, same analysis with .resu
         if diag is None:
            diag, stop = _diag_erre(resu)
      
         if stop:
            self.run.ASSERT(diag != None)
         else:
            # 2. ----- parse result file
            diag, tcpu, tsys, ttot = _diag_resu(resu, cas_test)
            # if resu doesn't exist, trying with .mess
            if diag is None:
               diag = _diag_mess(mess) or _('NO_RESU_FILE')
      
      return diag, tcpu, tsys, ttot

#-------------------------------------------------------------------------------
   def GetCModif(self, typ, fich):
      """
         Retourne, pour `typ` = 'capy'/'py', le nom du module et du package.
         `fich` est local.
      """
      para = {
         'capy'   : { 'comment' : '#&', 'nmin' : 3 },
         'py'     : { 'comment' : '#@', 'nmin' : 4 },
      }
      if typ not in para.keys():
         self._mess(_('invalid type : %s') % typ, _('<F>_PROGRAM_ERROR'))
      
      lig = open(fich, 'r').readline().split()
      if len(lig) < para[typ]['nmin'] or \
        (len(lig) > 0 and lig[0] != para[typ]['comment']):
         self._mess(_("invalid first line of file : %s\n" \
                      "      Should start with '%s' and have at least %s fields") \
            % (os.path.basename(fich), para[typ]['comment'], para[typ]['nmin']),
            _('<F>_INVALID_FILE'))
      
      if typ == 'py':
         mod = lig[2]
         rep = lig[3]
      elif typ == 'capy':
         mod = os.path.splitext(os.path.basename(fich))[0]
         rep = lig[2].lower()
      
      return mod, rep


#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
def get_txt_capy(capy):
   """
      On utilise le fait qu'il n'y a rien après la définition de l'OPER, PROC ou MACRO.
   """
   txt = open(capy, 'r').read()
   expr = re.compile('^[A-Za-z_0-9]+ *= *[OPERPROCMACROFORM]+ *\(.*',
                     re.MULTILINE | re.DOTALL)
   sans_op = expr.sub('', txt)
   keep = ['']
   for line in sans_op.splitlines():
      if not re.search('^ *#', line):
         keep.append(line)
   keep.append('')
   return os.linesep.join(keep)

#-------------------------------------------------------------------------------
def re_search(content, **kargs):
   """Search the regular expression `pattern` in content.
   If `string` is provided, it is escaped and `pattern` must contain "%s".
   """
   flag = kargs.get('flag', re.MULTILINE)
   what = kargs.get('result', 'bool')
   pattern = kargs.get('pattern', '%s')
   
   if kargs.get('string'):
      pattern = pattern % re.escape(kargs['string'])
   
   expr = re.compile(pattern, flag)
   
   if what == 'bool':
      res = expr.search(content) != None
   elif what == 'number':
      res = len(expr.findall(content))
   elif what == 'value':
      res = expr.findall(content)
   else:
      res = None
   return res

#-------------------------------------------------------------------------------
def _diag_erre(err):
   """
      Diagnostic à partir du fichier '.erre'.
   """
   diag = None
   stop = False
   if err is None or not os.path.exists(err):
      pass
   else:
      txt = open(err, 'r').read()
      
      n_debut = re_search(txt, string="-- CODE_ASTER -- VERSION", result='number')
      n_fin   = re_search(txt,
            string="""<I> <FIN> ARRET NORMAL DANS "FIN" PAR APPEL A "JEFINI".""",
            result='number')
      
      alarm = re_search(txt, pattern='^ *. *%s', string='<A>')
      fatal = re_search(txt, pattern='^ *. *%s', string='<F>')
      
      if fatal:
         expir = re_search(txt, pattern='^ *. *%s',
               string='<F> <INIAST> VERSION EXPIREE')
         superv = re_search(txt, pattern='^ *. *%s',
               string='<F> <SUPERVISEUR> ARRET SUR ERREUR(S) UTILISATEUR')
      else:
         expir  = False
         superv = False
      
      errs = re_search(txt, pattern='^ *. *%s', string='<S>')
      
      if errs:
         mem = re_search(txt, string='MEMOIRE INSUFFISANTE POUR ALLOUER')
         arretcpu = re_search(txt, pattern='^ *%s',
               string='ARRET PAR MANQUE DE TEMPS CPU')
      
      ok = n_debut == n_fin and n_fin > 0
      if ok:
         diag = 'OK'
         if alarm:
            diag = _('<A>_ALARM')
      # fatal error
      if fatal:
         diag = _('<F>_ERROR')
         if superv:
            diag = _('<F>_SUPERVISOR')
         elif expir:
            diag = _('<F>_EXPIRED_VERS')
      # "S" (recoverable) error
      if errs:
         diag = _('<S>_ERROR')
         if arretcpu:
            diag = _('<S>_CPU_LIMIT')
         elif mem:
            diag = _('<S>_MEMORY_ERROR')
      if not ok or fatal or errs:
         stop = True
   
   return diag, stop

#-------------------------------------------------------------------------------
def _diag_resu(resu, cas_test):
   """
      Diagnostic à partir du fichier '.resu'.
   """
   diag, tcpu, tsys, ttot = None, 0., 0., 0.
   # 2. ----- parse result file
   if resu is None or not os.path.exists(resu):
      pass
   else:
      # OK / NOOK
      ok = False
      nook = False
      # <A>_ALARME
      alarm = False
      diag0 = diag
      
      txt = open(resu, 'r').read()
      ok = re_search(txt, pattern='^ *OK +')
      nook = re_search(txt, pattern='^ *NOOK +')
      alarm = re_search(txt, pattern='^ *. *%s', string='<A>')
      
      value = re_search(txt, result='value',
            pattern='TOTAL_JOB +: +([0-9\.]+) +: +([0-9\.]+) +: +([0-9\.]+)')
      if len(value) > 0:
         tcpu, tsys, ttot = value[0]
      
      if nook:
         diag0 = 'NOOK_TEST_RESU'
      else:
         if cas_test and not ok:
            diag0 = 'NO_TEST_RESU'
         else:
            diag0 = 'OK'
            if alarm:
               diag0 = _('<A>_ALARM')
      diag = diag0
   
   return diag, tcpu, tsys, ttot

#-------------------------------------------------------------------------------
def _diag_mess(mess):
   """
      Essaie de trouver des pistes supplémentaires si le .mess existe.
   """
   diag = None
   if mess is None or not os.path.isfile(mess):
      pass
   else:
      txt = open(mess, 'r').read()
      syntax = re_search(txt, string="ERREUR A LA VERIFICATION SYNTAXIQUE") or \
               re_search(txt, pattern="ERREUR .*ACCAS")
      if syntax:
         diag = '<F>_SYNTAX_ERROR'
   
   return diag

