]> SALOME platform Git repositories - modules/kernel.git/blob - bin/salomeContext.py
Salome HOME
d4d44afdd8ed3cf77c908e2eeea9f98302144f9c
[modules/kernel.git] / bin / salomeContext.py
1 #! /usr/bin/env python3
2 # Copyright (C) 2013-2021  CEA/DEN, EDF R&D, OPEN CASCADE
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20
21 import os
22 import sys
23 import logging
24 import configparser
25
26 from parseConfigFile import parseConfigFile
27
28 import tempfile
29 import pickle
30 import subprocess
31 import sys
32 import platform
33
34 from salomeContextUtils import SalomeContextException
35
36 def usage():
37   msg = '''\
38 Usage: salome [command] [options] [--config=<file,folder,...>]
39
40 Commands:
41 =========
42     start           Start a new SALOME instance.
43     context         Initialize SALOME context. Current environment is extended.
44     shell           Initialize SALOME context, attached to the last created SALOME
45                     instance if any, and executes scripts passed as command arguments.
46                     User works in a Shell terminal. SALOME environment is set but
47                     application is not started.
48     connect         Connect a Python console to the active SALOME instance.
49     remote          run command in SALOME environment from remote call, ssh or rsh.
50     kill <port(s)>  Terminate SALOME instances running on given ports for current user.
51                     Port numbers must be separated by blank characters.
52     killall         Terminate *all* SALOME running instances for current user.
53                     Do not start a new one.
54     test            Run SALOME tests.
55     info            Display some information about SALOME.
56     doc <module(s)> Show online module documentation (if available).
57                     Module names must be separated by blank characters.
58     help            Show this message.
59
60 If no command is given, default is start.
61
62 Command options:
63 ================
64     Use salome <command> --help to show help on command. Available for the
65     following commands: start, shell, connect, test, info.
66
67 --config=<file,folder,...>
68 ==========================
69     Initialize SALOME context from a list of context files and/or a list
70     of folders containing context files. The list is comma-separated, without
71     any blank characters.
72 '''
73
74   print(msg)
75 #
76
77 """
78 The SalomeContext class in an API to configure SALOME context then
79 start SALOME using a single python command.
80
81 """
82 class SalomeContext:
83   """
84   Initialize context from a list of configuration files
85   identified by their names.
86   These files should be in appropriate .cfg format.
87   """
88   def __init__(self, configFileNames=0):
89     self.getLogger().setLevel(logging.INFO)
90     #it could be None explicitly (if user use multiples setVariable...for standalone)
91     if configFileNames is None:
92        return
93     configFileNames = configFileNames or []
94     if len(configFileNames) == 0:
95       raise SalomeContextException("No configuration files given")
96
97     reserved=['PATH', 'DYLD_FALLBACK_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_PATH', 'LIBRARY_PATH', 'QT_PLUGIN_PATH']
98     for filename in configFileNames:
99       basename, extension = os.path.splitext(filename)
100       if extension == ".cfg":
101         self.__setContextFromConfigFile(filename, reserved)
102       else:
103         self.getLogger().error("Unrecognized extension for configuration file: %s", filename)
104   #
105
106   def __loadEnvModules(self, env_modules):
107     modulecmd = os.getenv('LMOD_CMD')
108     if not modulecmd:
109       raise SalomeContextException("Module environment not present")
110       return
111     try:
112       out, err = subprocess.Popen([modulecmd, "python", "load"] + env_modules, stdout=subprocess.PIPE).communicate()
113       exec(out)  # define specific environment variables
114     except:
115       raise SalomeContextException("Failed to load env modules: %s ..." % ' '.join(env_modules))
116       pass
117   #
118
119   def runSalome(self, args):
120     import os
121     # Run this module as a script, in order to use appropriate Python interpreter
122     # according to current path (initialized from context files).
123     env_modules_option = "--with-env-modules="
124     env_modules_l = [x for x in args if x.startswith(env_modules_option)]
125     if env_modules_l:
126       env_modules = env_modules_l[-1][len(env_modules_option):].split(',')
127       self.__loadEnvModules(env_modules)
128       args = [x for x in args if not x.startswith(env_modules_option)]
129     else:
130       env_modules = os.getenv("SALOME_ENV_MODULES", None)
131       if env_modules:
132         self.__loadEnvModules(env_modules.split(','))
133
134     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
135     env_copy = os.environ.copy()
136     selfBytes= pickle.dumps(self, protocol=0)
137     argsBytes= pickle.dumps(args, protocol=0)
138     proc = subprocess.Popen(['python3', os.path.join(absoluteAppliPath,"bin","salome","salomeContext.py"), selfBytes.decode('latin1'), argsBytes.decode('latin1')], shell=False, close_fds=True, env=env_copy)
139     out, err = proc.communicate()
140     return out, err, proc.returncode
141   #
142
143   """Append value to PATH environment variable"""
144   def addToPath(self, value):
145     self.addToVariable('PATH', value)
146   #
147
148   """Append value to LD_LIBRARY_PATH environment variable"""
149   def addToLdLibraryPath(self, value):
150     if sys.platform == 'win32':
151       self.addToVariable('PATH', value)
152     elif  sys.platform == 'darwin':
153       if "LAPACK" in value:
154         self.addToVariable('DYLD_FALLBACK_LIBRARY_PATH', value)
155       else:
156         self.addToVariable('DYLD_LIBRARY_PATH', value)
157     else:
158       self.addToVariable('LD_LIBRARY_PATH', value)
159   #
160
161   """Append value to DYLD_LIBRARY_PATH environment variable"""
162   def addToDyldLibraryPath(self, value):
163     self.addToVariable('DYLD_LIBRARY_PATH', value)
164   #
165
166   """Append value to PYTHONPATH environment variable"""
167   def addToPythonPath(self, value):
168     self.addToVariable('PYTHONPATH', value)
169   #
170
171   """Set environment variable to value"""
172   def setVariable(self, name, value, overwrite=False):
173     env = os.getenv(name, '')
174     if env and not overwrite:
175       self.getLogger().error("Environment variable already existing (and not overwritten): %s=%s", name, value)
176       return
177
178     if env:
179       self.getLogger().debug("Overwriting environment variable: %s=%s", name, value)
180
181     value = os.path.expandvars(value) # expand environment variables
182     self.getLogger().debug("Set environment variable: %s=%s", name, value)
183     os.environ[name] = value
184   #
185
186   def setDefaultValue(self, name, value):
187     """ Set environment variable only if it is undefined."""
188     env = os.getenv(name, '')
189     if not env:
190       value = os.path.expandvars(value) # expand environment variables
191       self.getLogger().debug("Set environment variable: %s=%s", name, value)
192       os.environ[name] = value
193
194   """Unset environment variable"""
195   def unsetVariable(self, name):
196     if os.environ.has_key(name):
197       del os.environ[name]
198   #
199
200   """Append value to environment variable"""
201   def addToVariable(self, name, value, separator=os.pathsep):
202     if value == '':
203       return
204
205     value = os.path.expandvars(value) # expand environment variables
206     self.getLogger().debug("Add to %s: %s", name, value)
207     env = os.getenv(name, None)
208     if env is None:
209       os.environ[name] = value
210     else:
211       os.environ[name] = value + separator + env
212   #
213
214   ###################################
215   # This begins the private section #
216   ###################################
217
218   def __parseArguments(self, args):
219     if len(args) == 0 or args[0].startswith("-"):
220       return None, args
221
222     command = args[0]
223     options = args[1:]
224
225     availableCommands = {
226       'start'   : '_runAppli',
227       'context' : '_setContext',
228       'shell'   : '_runSession',
229       'remote'  : '_runRemote',
230       'connect' : '_runConsole',
231       'kill'    : '_kill',
232       'killall' : '_killAll',
233       'test'    : '_runTests',
234       'info'    : '_showInfo',
235       'doc'     : '_showDoc',
236       'help'    : '_usage',
237       'coffee'  : '_makeCoffee',
238       'car'     : '_getCar',
239       }
240
241     if command not in availableCommands:
242       command = "start"
243       options = args
244
245     return availableCommands[command], options
246   #
247
248   """
249   Run SALOME!
250   Args consist in a mandatory command followed by optional parameters.
251   See usage for details on commands.
252   """
253   def _startSalome(self, args):
254     import os
255     import sys
256     try:
257       from setenv import add_path
258       absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
259       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
260       add_path(path, "PYTHONPATH")
261       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
262       add_path(path, "PYTHONPATH")
263
264     except:
265       pass
266
267     command, options = self.__parseArguments(args)
268     sys.argv = options
269
270     if command is None:
271       if args and args[0] in ["-h","--help","help"]:
272         usage()
273         return 0
274       # try to default to "start" command
275       command = "_runAppli"
276
277     try:
278       res = getattr(self, command)(options) # run appropriate method
279       return res or 0
280     except SystemExit as ex:
281       if ex.code != 0:
282         self.getLogger().error("SystemExit %s in method %s.", ex.code, command)
283       return ex.code
284     except SalomeContextException as e:
285       self.getLogger().error(e)
286       return 1
287     except Exception:
288       self.getLogger().error("Unexpected error:")
289       import traceback
290       traceback.print_exc()
291       return 1
292   #
293
294   def __setContextFromConfigFile(self, filename, reserved=None):
295     mesa_root_dir = "MESA_ROOT_DIR"
296     if reserved is None:
297       reserved = []
298     try:
299       configInfo = parseConfigFile(filename, reserved)
300       unsetVars = configInfo.unsetVariables
301       configVars = configInfo.outputVariables
302       reservedDict = configInfo.reservedValues
303       defaultValues = configInfo.defaultValues
304     except SalomeContextException as e:
305       msg = "%s"%e
306       self.getLogger().error(msg)
307       return 1
308
309     # unset variables
310     for var in unsetVars:
311       self.unsetVariable(var)
312
313     # mesa stuff
314     if "MESA_GL_VERSION_OVERRIDE" in os.environ:
315       configVarsDict = {k:v for (k,v) in configVars}
316       if mesa_root_dir in configVarsDict:
317         path_to_mesa_lib = os.path.join(configVarsDict[mesa_root_dir],"lib")
318         if os.name == "posix":
319           self.addToVariable("LD_LIBRARY_PATH",path_to_mesa_lib)
320         else:
321           self.addToVariable("PATH",path_to_mesa_lib)
322
323     # set context
324     for reserved in reservedDict:
325       a = [_f for _f in reservedDict[reserved] if _f] # remove empty elements
326       a = [ os.path.realpath(x) for x in a ]
327       reformattedVals = os.pathsep.join(a)
328       if reserved in ["INCLUDE", "LIBPATH"]:
329         self.addToVariable(reserved, reformattedVals, separator=' ')
330       else:
331         self.addToVariable(reserved, reformattedVals)
332       pass
333
334
335     for key,val in configVars:
336       self.setVariable(key, val, overwrite=True)
337       pass
338
339     for key,val in defaultValues:
340       self.setDefaultValue(key, val)
341       pass
342
343     pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
344     pythonpath = [ os.path.realpath(x) for x in pythonpath ]
345     sys.path[:0] = pythonpath
346   #
347
348   def _runAppli(self, args=None):
349     if args is None:
350       args = []
351     # Initialize SALOME environment
352     sys.argv = ['runSalome'] + args
353     import setenv
354     setenv.main(True, exeName="salome start")
355
356     import runSalome
357     runSalome.runSalome()
358     return 0
359   #
360
361   def _setContext(self, args=None):
362     salome_context_set = os.getenv("SALOME_CONTEXT_SET")
363     if salome_context_set:
364       print("***")
365       print("*** SALOME context has already been set.")
366       print("*** Enter 'exit' (only once!) to leave SALOME context.")
367       print("***")
368       return 0
369
370     os.environ["SALOME_CONTEXT_SET"] = "yes"
371     print("***")
372     print("*** SALOME context is now set.")
373     print("*** Enter 'exit' (only once!) to leave SALOME context.")
374     print("***")
375
376     if sys.platform == 'win32':
377       cmd = ['cmd.exe']
378     else:
379       cmd = ["/bin/bash"]
380     proc = subprocess.Popen(cmd, shell=False, close_fds=True)
381     proc.communicate()
382     return proc.returncode
383   #
384
385   def _runSession(self, args=None):
386     if args is None:
387       args = []
388     sys.argv = ['runSession'] + args
389     import runSession
390     params, args = runSession.configureSession(args, exe="salome shell")
391
392     sys.argv = ['runSession'] + args
393     import setenv
394     setenv.main(True)
395
396     return runSession.runSession(params, args)
397   #
398
399   def _runRemote(self, args=None):
400     if args is None:
401       args = []
402 #   complete salome environment 
403     sys.argv = ['runRemote']
404     import setenv
405     setenv.main(True)
406
407     import runRemote
408     return runRemote.runRemote(args)
409   #
410
411   def _runConsole(self, args=None):
412     if args is None:
413       args = []
414     # Initialize SALOME environment
415     sys.argv = ['runConsole']
416     import setenv
417     setenv.main(True)
418
419     import runConsole
420     return runConsole.connect(args)
421   #
422
423   def _kill(self, args=None):
424     if args is None:
425       args = []
426     ports = args
427     if not ports:
428       print("Port number(s) not provided to command: salome kill <port(s)>")
429       return 1
430
431     import subprocess
432     sys.argv = ['kill']
433     import setenv
434     setenv.main(True)
435     if os.getenv("NSHOST") == "no_host":
436       os.unsetenv("NSHOST")
437     for port in ports:
438       if sys.platform == "win32":
439         proc = subprocess.Popen([os.getenv("PYTHONBIN"), "-m", "killSalomeWithPort", str(port)])
440       else:
441         proc = subprocess.Popen(["killSalomeWithPort.py", str(port)])
442       proc.communicate()
443
444     return 0
445   #
446
447   def _killAll(self, unused=None):
448     sys.argv = ['killAll']
449     import setenv
450     setenv.main(True)
451     if os.getenv("NSHOST") == "no_host":
452       os.unsetenv("NSHOST")
453     try:
454       import PortManager # mandatory
455       import subprocess
456       ports = PortManager.getBusyPorts()['this']
457
458       if ports:
459         for port in ports:
460           if sys.platform == "win32":
461             proc = subprocess.Popen([os.getenv("PYTHONBIN"), "-m", "killSalomeWithPort", str(port)])
462           else:
463             proc = subprocess.Popen(["killSalomeWithPort.py", str(port)])
464           proc.communicate()
465     except ImportError:
466       # :TODO: should be declared obsolete
467       from killSalome import killAllPorts
468       killAllPorts()
469       pass
470     return 0
471   #
472
473   def _runTests(self, args=None):
474     if args is None:
475       args = []
476     sys.argv = ['runTests']
477     import setenv
478     setenv.main(True)
479
480     import runTests
481     return runTests.runTests(args, exe="salome test")
482   #
483
484   def _showSoftwareVersions(self, softwares=None):
485     config = configparser.SafeConfigParser()
486     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
487     filename = os.path.join(absoluteAppliPath, "sha1_collections.txt")
488     versions = {}
489     max_len = 0
490     with open(filename) as f:
491       for line in f:
492         try:
493           software, version, sha1 = line.split()
494           versions[software.upper()] = version
495           if len(software) > max_len:
496             max_len = len(software)
497         except:
498           pass
499         pass
500       pass
501     if softwares:
502       for soft in softwares:
503         if soft.upper() in versions:
504           print(soft.upper().rjust(max_len), versions[soft.upper()])
505     else:
506       import collections
507       od = collections.OrderedDict(sorted(versions.items()))
508       for name, version in od.items():
509         print(name.rjust(max_len), versions[name])
510     pass
511
512   def _showInfo(self, args=None):
513     if args is None:
514       args = []
515
516     usage = "Usage: salome info [options]"
517     epilog  = """\n
518 Display some information about SALOME.\n
519 Available options are:
520     -p,--ports                     Show the list of busy ports (running SALOME instances).
521     -s,--softwares [software(s)]   Show the list and versions of SALOME softwares.
522                                    Software names must be separated by blank characters.
523                                    If no software is given, show version of all softwares.
524     -v,--version                   Show running SALOME version.
525     -h,--help                      Show this message.
526 """
527     if not args:
528       args = ["--version"]
529
530     if "-h" in args or "--help" in args:
531       print(usage + epilog)
532       return 0
533
534     if "-p" in args or "--ports" in args:
535       import PortManager
536       ports = PortManager.getBusyPorts()
537       this_ports = ports['this']
538       other_ports = ports['other']
539       if this_ports or other_ports:
540           print("SALOME instances are running on the following ports:")
541           if this_ports:
542               print("   This application:", this_ports)
543           else:
544               print("   No SALOME instances of this application")
545           if other_ports:
546               print("   Other applications:", other_ports)
547           else:
548               print("   No SALOME instances of other applications")
549       else:
550           print("No SALOME instances are running")
551
552     if "-s" in args or "--softwares" in args:
553       if "-s" in args:
554         index = args.index("-s")
555       else:
556         index = args.index("--softwares")
557       indexEnd=index+1
558       while indexEnd < len(args) and args[indexEnd][0] != "-":
559         indexEnd = indexEnd + 1
560       self._showSoftwareVersions(softwares=args[index+1:indexEnd])
561
562     if "-v" in args or "--version" in args:
563       print("Running with python", platform.python_version())
564       return self._runAppli(["--version"])
565
566     return 0
567   #
568
569   def _showDoc(self, args=None):
570     if args is None:
571       args = []
572
573     modules = args
574     if not modules:
575       print("Module(s) not provided to command: salome doc <module(s)>")
576       return 1
577
578     appliPath = os.getenv("ABSOLUTE_APPLI_PATH")
579     if not appliPath:
580       raise SalomeContextException("Unable to find application path. Please check that the variable ABSOLUTE_APPLI_PATH is set.")
581     baseDir = os.path.join(appliPath, "share", "doc", "salome")
582     for module in modules:
583       docfile = os.path.join(baseDir, "gui", module.upper(), "index.html")
584       if not os.path.isfile(docfile):
585         docfile = os.path.join(baseDir, "tui", module.upper(), "index.html")
586       if not os.path.isfile(docfile):
587         docfile = os.path.join(baseDir, "dev", module.upper(), "index.html")
588       if os.path.isfile(docfile):
589         out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
590       else:
591         print("Online documentation is not accessible for module:", module)
592
593   def _usage(self, unused=None):
594     usage()
595   #
596
597   def _makeCoffee(self, unused=None):
598     print("                        (")
599     print("                          )     (")
600     print("                   ___...(-------)-....___")
601     print("               .-\"\"       )    (          \"\"-.")
602     print("         .-\'``\'|-._             )         _.-|")
603     print("        /  .--.|   `\"\"---...........---\"\"`   |")
604     print("       /  /    |                             |")
605     print("       |  |    |                             |")
606     print("        \\  \\   |                             |")
607     print("         `\\ `\\ |                             |")
608     print("           `\\ `|            SALOME           |")
609     print("           _/ /\\            4 EVER           /")
610     print("          (__/  \\             <3            /")
611     print("       _..---\"\"` \\                         /`\"\"---.._")
612     print("    .-\'           \\                       /          \'-.")
613     print("   :               `-.__             __.-\'              :")
614     print("   :                  ) \"\"---...---\"\" (                 :")
615     print("    \'._               `\"--...___...--\"`              _.\'")
616     print("      \\\"\"--..__                              __..--\"\"/")
617     print("       \'._     \"\"\"----.....______.....----\"\"\"     _.\'")
618     print("          `\"\"--..,,_____            _____,,..--\"\"`")
619     print("                        `\"\"\"----\"\"\"`")
620     print("")
621     print("                    SALOME is working for you; what else?")
622     print("")
623   #
624
625   def _getCar(self, unused=None):
626     print("                                              _____________")
627     print("                                  ..---:::::::-----------. ::::;;.")
628     print("                               .\'\"\"\"\"\"\"                  ;;   \\  \":.")
629     print("                            .\'\'                          ;     \\   \"\\__.")
630     print("                          .\'                            ;;      ;   \\\\\";")
631     print("                        .\'                              ;   _____;   \\\\/")
632     print("                      .\'                               :; ;\"     \\ ___:\'.")
633     print("                    .\'--...........................    : =   ____:\"    \\ \\")
634     print("               ..-\"\"                               \"\"\"\'  o\"\"\"     ;     ; :")
635     print("          .--\"\"  .----- ..----...    _.-    --.  ..-\"     ;       ;     ; ;")
636     print("       .\"\"_-     \"--\"\"-----\'\"\"    _-\"        .-\"\"         ;        ;    .-.")
637     print("    .\'  .\'   SALOME             .\"         .\"              ;       ;   /. |")
638     print("   /-./\'         4 EVER <3    .\"          /           _..  ;       ;   ;;;|")
639     print("  :  ;-.______               /       _________==.    /_  \\ ;       ;   ;;;;")
640     print("  ;  / |      \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\"          :    /\" \". |;       ; _; ;;;")
641     print(" /\"-/  |                /   /                  /   /     ;|;      ;-\" | ;\';")
642     print(":-  :   \"\"\"----______  /   /              ____.   .  .\"\'. ;;   .-\"..T\"   .")
643     print("\'. \"  ___            \"\":   \'\"\"\"\"\"\"\"\"\"\"\"\"\"\"    .   ; ;    ;; ;.\" .\"   \'--\"")
644     print(" \",   __ \"\"\"  \"\"---... :- - - - - - - - - \' \'  ; ;  ;    ;;\"  .\"")
645     print("  /. ;  \"\"\"---___                             ;  ; ;     ;|.\"\"")
646     print(" :  \":           \"\"\"----.    .-------.       ;   ; ;     ;:")
647     print("  \\  \'--__               \\   \\        \\     /    | ;     ;;")
648     print("   \'-..   \"\"\"\"---___      :   .______..\\ __/..-\"\"|  ;   ; ;")
649     print("       \"\"--..       \"\"\"--\"        m l s         .   \". . ;")
650     print("             \"\"------...                  ..--\"\"      \" :")
651     print("                        \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"    \\        /")
652     print("                                               \"------\"")
653     print("")
654     print("                                Drive your simulation properly with SALOME!")
655     print("")
656   #
657
658   # Add the following two methods since logger is not pickable
659   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
660   def __getstate__(self):
661     d = dict(self.__dict__)
662     if hasattr(self, '_logger'):
663       del d['_logger']
664     return d
665   #
666   def __setstate__(self, d):
667     self.__dict__.update(d) # I *think* this is a safe way to do it
668   #
669   # Excluding self._logger from pickle operation imply using the following method to access logger
670   def getLogger(self):
671     if not hasattr(self, '_logger'):
672       self._logger = logging.getLogger(__name__)
673       #self._logger.setLevel(logging.DEBUG)
674       #self._logger.setLevel(logging.WARNING)
675       self._logger.setLevel(logging.ERROR)
676     return self._logger
677   #
678
679 if __name__ == "__main__":
680   if len(sys.argv) == 3:
681     context = pickle.loads(sys.argv[1].encode('latin1'))
682     args = pickle.loads(sys.argv[2].encode('latin1'))
683
684     status = context._startSalome(args)
685     sys.exit(status)
686   else:
687     usage()
688 #