Salome HOME
Fix return statement for 'salome context'
[modules/kernel.git] / bin / launchConfigureParser.py
index 1a7298ee5755f1f73c41e83a3e07c9a84ced79dd..57c492797c978a1bb50f293b790b5fdb32688f70 100755 (executable)
@@ -1,5 +1,5 @@
 #  -*- coding: iso-8859-1 -*-
-# Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
+# Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
 #
 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -7,7 +7,7 @@
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
-# version 2.1 of the License.
+# version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -26,7 +26,7 @@ import xml.sax
 import optparse
 import types
 
-from salome_utils import verbose, setVerbose, getPortNumber, getHomeDir
+from salome_utils import verbose, getPortNumber, getHomeDir
 
 # names of tags in XML configuration file
 doc_tag = "document"
@@ -60,6 +60,7 @@ pinter_nam     = "pinter"
 batch_nam      = "batch"
 test_nam       = "test"
 play_nam       = "play"
+lang_nam       = "language"
 gdb_session_nam = "gdb_session"
 ddd_session_nam = "ddd_session"
 valgrind_session_nam = "valgrind_session"
@@ -81,7 +82,7 @@ script_nam     = "pyscript"
 
 # possible choices for the "embedded" and "standalone" parameters
 embedded_choices   = [ "registry", "study", "moduleCatalog", "cppContainer", "SalomeAppEngine" ]
-standalone_choices = [ "registry", "study", "moduleCatalog", "cppContainer", "pyContainer"]
+standalone_choices = [ "registry", "study", "moduleCatalog", "cppContainer"]
 
 # values of boolean type (must be '0' or '1').
 # xml_parser.boolValue() is used for correct setting
@@ -99,11 +100,13 @@ def version():
     try:
         filename = None
         root_dir = os.environ.get( 'KERNEL_ROOT_DIR', '' ) # KERNEL_ROOT_DIR or "" if not found
-        if root_dir and os.path.exists( root_dir + "/bin/salome/VERSION" ):
-            filename = root_dir + "/bin/salome/VERSION"
+        version_file = os.path.join(root_dir, 'bin', 'salome', 'VERSION')
+        if root_dir and os.path.exists( version_file ):
+            filename = version_file
         root_dir = os.environ.get( 'GUI_ROOT_DIR', '' )    # GUI_ROOT_DIR "" if not found
-        if root_dir and os.path.exists( root_dir + "/bin/salome/VERSION" ):
-            filename = root_dir + "/bin/salome/VERSION"
+        version_file = os.path.join(root_dir, 'bin', 'salome', 'VERSION')
+        if root_dir and os.path.exists( version_file ):
+            filename = version_file
         if filename:
             str = open( filename, "r" ).readline() # str = "THIS IS SALOME - SALOMEGUI VERSION: 3.0.0"
             match = re.search( r':\s+([a-zA-Z0-9.]+)\s*$', str )
@@ -120,8 +123,19 @@ def version():
 def version_id(fname):
     major = minor = release = dev1 = dev2 = 0
     vers = fname.split(".")
-    if len(vers) > 0: major = int(vers[0])
-    if len(vers) > 1: minor = int(vers[1])
+    if len(vers) > 0:
+      try:
+        major = int(vers[0])
+      except ValueError:
+        # If salome version given is DEV, the call to int('DEV') will fail with
+        # a ValueError exception
+        pass
+    try:
+      if len(vers) > 1: minor = int(vers[1])
+    except ValueError:
+      # If salome version given is 7.DEV, the call to int('DEV') will fail with
+      # a ValueError exception
+      pass
     if len(vers) > 2:
         mr = re.search(r'^([0-9]+)([A-Z]|RC)?([0-9]*)',vers[2], re.I)
         if mr:
@@ -154,18 +168,13 @@ def version_id(fname):
 ###
 def defaultUserFile(appname=salomeappname, cfgname=salomecfgname):
     v = version()
-    if v: v = ".%s" % v
-    if sys.platform == "win32":
-      filename = os.path.join(getHomeDir(), "%s.xml%s" % (appname, v))
-    else:
-        if cfgname:
-            filename = os.path.join(getHomeDir(), ".config", cfgname, "%src%s" % (appname, v))
-            pass
-        else:
-            filename = os.path.join(getHomeDir(), ".%src%s" % (appname, v))
-            pass
-        pass
-    return filename
+    filetmpl = sys.platform == "win32" and "{0}.xml.{1}" or "{0}rc.{1}"
+    paths = []
+    paths.append(getHomeDir())
+    paths.append(".config")
+    if cfgname: paths.append(cfgname)
+    paths.append(filetmpl.format(appname, v))
+    return os.path.join(*paths)
 
 ###
 # Get user configuration file name
@@ -189,19 +198,20 @@ def userFile(appname, cfgname):
     if not id0: return None                      # bad version id -> can't detect appropriate file
 
     # ... get all existing user preferences files
-    if sys.platform == "win32":
-        files = glob.glob(os.path.join(getHomeDir(), "%s.xml.*" % appname))
-    else:
-        files = []
-        if cfgname:
-            # Since v6.6.0 - in ~/.config/salome directory, without dot prefix
-            files += glob.glob(os.path.join(getHomeDir(), ".config", cfgname, "%src.*" % appname))
-            # Since v6.5.0 - in ~/.config/salome directory, dot-prefixed (backward compatibility)
-            files += glob.glob(os.path.join(getHomeDir(), ".config", cfgname, ".%src.*" % appname))
-            pass
-        # old style (before v6.5.0) - in ~ directory, dot-prefixed
-        files += glob.glob(os.path.join(getHomeDir(), ".%src.*" % appname))
+    filetmpl1 = sys.platform == "win32" and "{0}.xml.*" or "{0}rc.*"
+    filetmpl2 = sys.platform == "win32" and filetmpl1 or "." + filetmpl1
+    files = []
+    if cfgname:
+        # Since v6.6.0 - in ~/.config/salome directory, without dot prefix
+        files += glob.glob(os.path.join(getHomeDir(), ".config", cfgname, filetmpl1.format(appname)))
+        # Since v6.5.0 - in ~/.config/salome directory, dot-prefixed (backward compatibility)
+        if filetmpl2 and filetmpl2 != filetmpl1:
+            files += glob.glob(os.path.join(getHomeDir(), ".config", cfgname, filetmpl2.format(appname)))
         pass
+    # old style (before v6.5.0) - in ~ directory, dot-prefixed
+    if filetmpl2 and filetmpl2 != filetmpl1:
+        files += glob.glob(os.path.join(getHomeDir(), filetmpl2.format(appname)))
+    pass
 
     # ... loop through all files and find most appopriate file (with closest id)
     appr_id   = -1
@@ -209,9 +219,9 @@ def userFile(appname, cfgname):
     for f in files:
         ff = os.path.basename( f )
         if sys.platform == "win32":
-            match = re.search( r'^%s\.xml\.([a-zA-Z0-9.]+)$'%appname, ff )
+            match = re.search( r'^{0}\.xml\.([a-zA-Z0-9.]+)$'.format(appname), ff )
         else:
-            match = re.search( r'^\.?%src\.([a-zA-Z0-9.]+)$'%appname, ff )
+            match = re.search( r'^\.?{0}rc\.([a-zA-Z0-9.]+)$'.format(appname), ff )
         if match:
             ver = version_id(match.group(1))
             if not ver: continue                 # bad version id -> skip file
@@ -252,7 +262,8 @@ def process_containers_params( standalone, embedded ):
 section_to_skip = ""
 
 class xml_parser:
-    def __init__(self, fileName, _opts, _importHistory=[] ):
+    def __init__(self, fileName, _opts, _importHistory):
+        #warning _importHistory=[] is NOT good: is NOT empty,reinitialized after first call
         if verbose(): print "Configure parser: processing %s ..." % fileName
         self.fileName = os.path.abspath(fileName)
         self.importHistory = _importHistory
@@ -299,6 +310,15 @@ class xml_parser:
         return strloc
         pass
 
+    def strValue( self, str ):
+        strloc = str
+        try:
+            if isinstance(strloc, types.UnicodeType): strloc = strloc.encode().strip()
+            else: strloc = strloc.strip()
+        except:
+            pass
+        return strloc
+
     def startElement(self, name, attrs):
         self.space.append(name)
         self.current = None
@@ -311,7 +331,7 @@ class xml_parser:
         # either "launch" or module name -- set section_name
         if self.space == [doc_tag, sec_tag] and nam_att in attrs.getNames():
             section_name = attrs.getValue( nam_att )
-            if section_name == lanch_nam:
+            if section_name in [lanch_nam, lang_nam]:
                 self.section = section_name # launch section
             elif self.opts.has_key( modules_nam ) and \
                  section_name in self.opts[ modules_nam ]:
@@ -333,19 +353,20 @@ class xml_parser:
                 key = nam
             else:                         # key for <module> section
                 key = self.section + "_" + nam
+            key = self.strValue( key )
             if nam in boolKeys:
                 self.opts[key] = self.boolValue( val )  # assign boolean value: 0 or 1
             elif nam in intKeys:
                 self.opts[key] = self.intValue( val )   # assign integer value
             elif nam in listKeys:
-                self.opts[key] = filter( lambda a: a.strip(), re.split( "[:;,]", val ) ) # assign list value: []
+                self.opts[key] = [ self.strValue( a ) for a in re.split( "[:;,]", val ) ] # assign list value: []
             else:
-                self.opts[key] = val
+                self.opts[key] = self.strValue( val ) # string value
             pass
         pass
 
     def endElement(self, name):
-        p = self.space.pop()
+        self.space.pop()
         self.current = None
         if self.section != section_to_skip and name == sec_tag:
             self.section = section_to_skip
@@ -476,7 +497,9 @@ def store_boolean (option, opt, value, parser, *args):
         for attribute in args:
             setattr(parser.values, attribute, value)
 
-def CreateOptionParser (theAdditionalOptions=[]):
+def CreateOptionParser (theAdditionalOptions=None, exeName=None):
+    if theAdditionalOptions is None:
+        theAdditionalOptions = []
     # GUI/Terminal. Default: GUI
     help_str = "Launch without GUI (in the terminal mode)."
     o_t = optparse.Option("-t",
@@ -536,21 +559,6 @@ def CreateOptionParser (theAdditionalOptions=[]):
                           dest="log_file",
                           help=help_str)
 
-    # Execute python scripts. Default: None.
-    help_str  = "Python script(s) to be imported. Python scripts are imported "
-    help_str += "in the order of their appearance. In GUI mode python scripts "
-    help_str += "are imported in the embedded python interpreter of current study, "
-    help_str += "otherwise in an external python interpreter. "
-    help_str += "Note: this option is obsolete. Instead you can pass Python script(s) "
-    help_str += "directly as positional parameter."
-    o_u = optparse.Option("-u",
-                          "--execute",
-                          metavar="<script1,script2,...>",
-                          type="string",
-                          action="append",
-                          dest="py_scripts",
-                          help=help_str)
-
     # Configuration XML file. Default: see defaultUserFile() function
     help_str  = "Parse application settings from the <file> "
     help_str += "instead of default %s" % defaultUserFile()
@@ -736,7 +744,8 @@ def CreateOptionParser (theAdditionalOptions=[]):
     help_str += "0 to keep the standalone servers as daemon [default]. "
     help_str += "This option is only useful in batchmode "
     help_str += "(terminal mode or without showing desktop)."
-    o_shutdown = optparse.Option("--shutdown-servers",
+    o_shutdown = optparse.Option("-w",
+                                 "--shutdown-servers",
                                  metavar="<1/0>",
                                  #type="choice", choices=boolean_choices,
                                  type="string",
@@ -781,8 +790,16 @@ def CreateOptionParser (theAdditionalOptions=[]):
     o_port = optparse.Option("--port",
                              metavar="<port>",
                              type="int",
-                             action="store",
+                                   action="store",
                              dest="use_port",
+                                   help=help_str)
+
+    help_str  = "Force application language. By default, a language specified in "
+    help_str += "the user's preferences is used."
+    o_lang = optparse.Option("-a",
+                             "--language",
+                             action="store",
+                             dest="language",
                              help=help_str)
 
     # All options
@@ -790,7 +807,6 @@ def CreateOptionParser (theAdditionalOptions=[]):
                 o_d,o_o, # Desktop
                 o_b,     # Batch
                 o_l,o_f, # Use logger or log-file
-                o_u,     # Execute python scripts
                 o_r,     # Configuration XML file
                 o_x,     # xterm
                 o_m,     # Modules
@@ -815,16 +831,22 @@ def CreateOptionParser (theAdditionalOptions=[]):
                 o_wake_up,
                 o_slm,   # Server launch mode
                 o_port,  # Use port
+                o_lang,  # Language
                 ]
 
-    #std_options = ["gui", "desktop", "log_file", "py_scripts", "resources",
+    #std_options = ["gui", "desktop", "log_file", "resources",
     #               "xterm", "modules", "embedded", "standalone",
     #               "portkill", "killall", "interp", "splash",
     #               "catch_exceptions", "print_port", "save_config", "ns_port_log_file"]
 
     opt_list += theAdditionalOptions
 
-    a_usage = "%prog [options] [STUDY_FILE] [PYTHON_FILE [PYTHON_FILE ...]]"
+    if not exeName:
+      exeName = "%prog"
+
+    a_usage = """%s [options] [STUDY_FILE] [PYTHON_FILE [args] [PYTHON_FILE [args]...]]
+Python file arguments, if any, must be comma-separated (without blank characters) and prefixed by "args:" (without quotes), e.g. myscript.py args:arg1,arg2=val,...
+"""%exeName
     version_str = "Salome %s" % version()
     pars = optparse.OptionParser(usage=a_usage, version=version_str, option_list=opt_list)
 
@@ -840,7 +862,7 @@ def CreateOptionParser (theAdditionalOptions=[]):
 args = {}
 #def get_env():
 #args = []
-def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgname):
+def get_env(theAdditionalOptions=None, appname=salomeappname, cfgname=salomecfgname, exeName=None):
     ###
     # Collect launch configuration files:
     # - The environment variable "<appname>Config" (SalomeAppConfig) which can
@@ -863,27 +885,24 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
     #   specified in configuration file(s)
     ###
 
+    if theAdditionalOptions is None:
+        theAdditionalOptions = []
+
     global args
     config_var = appname+'Config'
 
-    separator = ":"
-    if os.sys.platform == 'win32':
-        separator = ";"
-
     # check KERNEL_ROOT_DIR
-    try:
-        kernel_root_dir=os.environ["KERNEL_ROOT_DIR"]
-    except:
+    kernel_root_dir = os.environ.get("KERNEL_ROOT_DIR", None)
+    if kernel_root_dir is None:
         print """
         For each SALOME module, the environment variable <moduleN>_ROOT_DIR must be set.
         KERNEL_ROOT_DIR is mandatory.
         """
         sys.exit(1)
-        pass
 
     ############################
     # parse command line options
-    pars = CreateOptionParser(theAdditionalOptions)
+    pars = CreateOptionParser(theAdditionalOptions, exeName=exeName)
     (cmd_opts, cmd_args) = pars.parse_args(sys.argv[1:])
     ############################
 
@@ -892,6 +911,13 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
         from searchFreePort import searchFreePort
         searchFreePort({})
         print "port:%s"%(os.environ['NSPORT'])
+
+        try:
+            import PortManager
+            PortManager.releasePort(os.environ['NSPORT'])
+        except ImportError:
+            pass
+
         sys.exit(0)
         pass
 
@@ -899,20 +925,23 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
     dirs = []
     if os.getenv(config_var):
         if sys.platform == 'win32':
-            dirs += re.split(';', os.getenv(config_var))
+            dirs += re.split(os.pathsep, os.getenv(config_var))
         else:
             dirs += re.split('[;|:]', os.getenv(config_var))
 
-    gui_available = True
-    if os.getenv("GUI_ROOT_DIR") and os.path.isdir( os.getenv("GUI_ROOT_DIR") + "/share/salome/resources/gui" ):
-        dirs += [os.getenv("GUI_ROOT_DIR") + "/share/salome/resources/gui"]
+    gui_available = False
+    if os.getenv("GUI_ROOT_DIR"):
+        gui_resources_dir = os.path.join(os.getenv("GUI_ROOT_DIR"),'share','salome','resources','gui')
+        if os.path.isdir( gui_resources_dir ):
+            gui_available = True
+            dirs.append(gui_resources_dir)
         pass
-    else:
-        gui_available = False
-        if os.getenv("KERNEL_ROOT_DIR") and os.path.isdir( os.getenv("KERNEL_ROOT_DIR") + "/bin/salome/appliskel" ):
-            dirs += [os.getenv("KERNEL_ROOT_DIR") + "/bin/salome/appliskel"]
+    if not gui_available:
+        kernel_resources_dir = os.path.join(os.getenv("KERNEL_ROOT_DIR"),'bin','salome','appliskel')
+        if os.getenv("KERNEL_ROOT_DIR") and os.path.isdir( kernel_resources_dir ):
+          dirs.append(kernel_resources_dir)
         pass
-    os.environ[config_var] = separator.join(dirs)
+    os.environ[config_var] = os.pathsep.join(dirs)
 
     dirs.reverse() # reverse order, like in "path" variable - FILO-style processing
 
@@ -930,7 +959,7 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
             if verbose(): print "Configure parser: Warning : can not find configuration file %s" % filename
         else:
             try:
-                p = xml_parser(filename, _opts)
+                p = xml_parser(filename, _opts, [])
                 _opts = p.opts
             except:
                 if verbose(): print "Configure parser: Error : can not read configuration file %s" % filename
@@ -948,7 +977,7 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
         if verbose(): print "Configure parser: Warning : can not find user configuration file"
     else:
         try:
-            p = xml_parser(user_config, _opts)
+            p = xml_parser(user_config, _opts, [])
             _opts = p.opts
         except:
             if verbose(): print 'Configure parser: Error : can not read user configuration file'
@@ -962,15 +991,15 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
     # set default values for options which are NOT set in config files
     for aKey in listKeys:
         if not args.has_key( aKey ):
-            args[aKey]=[]
+            args[aKey] = []
 
     for aKey in boolKeys:
         if not args.has_key( aKey ):
-            args[aKey]=0
+            args[aKey] = 0
 
     if args[file_nam]:
         afile=args[file_nam]
-        args[file_nam]=[afile]
+        args[file_nam] = [afile]
 
     args[appname_nam] = appname
 
@@ -983,7 +1012,7 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
     # apply command-line options to the arguments
     # each option given in command line overrides the option from xml config file
     #
-    # Options: gui, desktop, log_file, py_scripts, resources,
+    # Options: gui, desktop, log_file, resources,
     #          xterm, modules, embedded, standalone,
     #          portkill, killall, interp, splash,
     #          catch_exceptions, pinter
@@ -1023,22 +1052,21 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
     if cmd_opts.ns_port_log_file is not None:
       args["ns_port_log_file"] = cmd_opts.ns_port_log_file
 
-    # Python scripts
-    args[script_nam] = []
-    if cmd_opts.py_scripts is not None:
-        listlist = cmd_opts.py_scripts
-        for listi in listlist:
-            if os.sys.platform == 'win32':
-                args[script_nam] += re.split( "[;,]", listi)
-            else:
-                args[script_nam] += re.split( "[:;,]", listi)
+    # Study files
     for arg in cmd_args:
-        if arg[-3:] == ".py":
-            args[script_nam].append(arg)
-        elif not args["study_hdf"]:
+        if arg[-4:] == ".hdf" and not args["study_hdf"]:
             args["study_hdf"] = arg
-            pass
-        pass
+
+    # Python scripts
+    from salomeContextUtils import getScriptsAndArgs, ScriptAndArgs
+    args[script_nam] = getScriptsAndArgs(cmd_args)
+    if args[gui_nam] and args["session_gui"]:
+        new_args = []
+        for sa_obj in args[script_nam]: # args[script_nam] is a list of ScriptAndArgs objects
+            script = re.sub(r'^python.*\s+', r'', sa_obj.script)
+            new_args.append(ScriptAndArgs(script=script, args=sa_obj.args, out=sa_obj.out))
+        #
+        args[script_nam] = new_args
 
     # xterm
     if cmd_opts.xterm is not None: args[xterm_nam] = cmd_opts.xterm
@@ -1125,7 +1153,7 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
     ####################################################
     # Add <theAdditionalOptions> values to args
     for add_opt in theAdditionalOptions:
-        cmd = "args[\"%s\"] = cmd_opts.%s"%(add_opt.dest,add_opt.dest)
+        cmd = "args[\"{0}\"] = cmd_opts.{0}".format(add_opt.dest)
         exec(cmd)
     ####################################################
 
@@ -1140,16 +1168,19 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
         dirs = re.split('[;]', os.environ[config_var] )
     else:
         dirs = re.split('[;|:]', os.environ[config_var] )
-    for m in args[modules_nam]:
-        if m not in ["KERNEL", "GUI", ""] and os.getenv("%s_ROOT_DIR"%m):
-            d1 = os.getenv("%s_ROOT_DIR"%m) + "/share/salome/resources/" + m.lower()
-            d2 = os.getenv("%s_ROOT_DIR"%m) + "/share/salome/resources"
+    for module in args[modules_nam]:
+        if module not in ["KERNEL", "GUI", ""] and os.getenv("{0}_ROOT_DIR".format(module)):
+            d1 = os.path.join(os.getenv("{0}_ROOT_DIR".format(module)),"share","salome","resources",module.lower())
+            d2 = os.path.join(os.getenv("{0}_ROOT_DIR".format(module)),"share","salome","resources")
             #if os.path.exists( "%s/%s.xml"%(d1, appname) ):
-            if os.path.exists( "%s/%s.xml"%(d1, salomeappname) ):
+            if os.path.exists( os.path.join(d1,"{0}.xml".format(salomeappname)) ):
                 dirs.append( d1 )
             #elif os.path.exists( "%s/%s.xml"%(d2, appname) ):
-            elif os.path.exists( "%s/%s.xml"%(d2, salomeappname) ):
+            elif os.path.exists( os.path.join(d2,"{0}.xml".format(salomeappname)) ):
                 dirs.append( d2 )
+        else:
+            #print "* '"+m+"' should be deleted from ",args[modules_nam]
+            pass
 
     # Test
     if cmd_opts.test_script_file is not None:
@@ -1176,7 +1207,14 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
             sys.exit(1)
         args[useport_nam] = cmd_opts.use_port
 
+    if cmd_opts.language is not None:
+        langs = args["language_languages"] if "language_languages" in args else []
+        if cmd_opts.language not in langs:
+            print "Error: unsupported language: %s" % cmd_opts.language
+            sys.exit(1)
+        args[lang_nam] = cmd_opts.language
+
     # return arguments
-    os.environ[config_var] = separator.join(dirs)
+    os.environ[config_var] = os.pathsep.join(dirs)
     #print "Args: ", args
     return args