]> SALOME platform Git repositories - modules/yacs.git/blob - bin/salomeContext.py
Salome HOME
add SalomeInstance object
[modules/yacs.git] / bin / salomeContext.py
1 # Copyright (C) 2013-2015  CEA/DEN, EDF R&D, OPEN CASCADE
2 #
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20 import os
21 import sys
22 import logging
23 import ConfigParser
24
25 from parseConfigFile import parseConfigFile
26 from parseConfigFile import convertEnvFileToConfigFile
27
28 import tempfile
29 import pickle
30 import subprocess
31 import platform
32
33 from salomeContextUtils import SalomeContextException
34
35 def usage():
36   #exeName = os.path.splitext(os.path.basename(__file__))[0]
37
38   msg = '''\
39 Usage: salome [command] [options] [--config=<file,folder,...>]
40
41 Commands:
42 =========
43     start           Starts a SALOME session (through virtual application)
44     context         Initializes SALOME context.
45     shell           Initializes SALOME context, and executes scripts passed
46                     as command arguments
47     connect         Connects a Python console to the active SALOME session
48     kill <port(s)>  Terminate SALOME session running on given ports for current user
49                     Port numbers must be separated by blank characters
50     killall         Kill *all* SALOME running sessions for current user
51     test            Run SALOME tests.
52     info            Display some information about SALOME
53     help            Show this message
54     coffee          Yes! SALOME can also make coffee!!
55
56 If no command is given, default to start.
57
58 Command options:
59 ================
60     Use salome <command> --help to show help on command ; available for commands:
61     start, shell, test.
62
63 --config=<file,folder,...>
64 ==========================
65     Initialize SALOME context from a list of context files and/or a list
66     of folders containing context files. The list is comma-separated, whithout
67     any blank characters.
68 '''
69
70   print msg
71 #
72
73 """
74 The SalomeContext class in an API to configure SALOME context then
75 start SALOME using a single python command.
76
77 """
78 class SalomeContext:
79   """
80   Initialize context from a list of configuration files
81   identified by their names.
82   These files should be in appropriate (new .cfg) format.
83   However you can give old .sh environment files; in this case,
84   the SalomeContext class will try to automatically convert them
85   to .cfg format before setting the context.
86   """
87   def __init__(self, configFileNames=0):
88     #it could be None explicitely (if user use multiples setVariable...for standalone)
89     if configFileNames is None:
90        return
91     configFileNames = configFileNames or []
92     if len(configFileNames) == 0:
93       raise SalomeContextException("No configuration files given")
94
95     reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_PATH']
96     for filename in configFileNames:
97       basename, extension = os.path.splitext(filename)
98       if extension == ".cfg":
99         self.__setContextFromConfigFile(filename, reserved)
100       elif extension == ".sh":
101         #new convert procedures, temporary could be use not to be automatically deleted
102         #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
103         temp = tempfile.NamedTemporaryFile(suffix='.cfg')
104         try:
105           convertEnvFileToConfigFile(filename, temp.name, reserved)
106           self.__setContextFromConfigFile(temp.name, reserved)
107           temp.close()
108         except (ConfigParser.ParsingError, ValueError) as e:
109           self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
110           temp.close()
111           sys.exit(1)
112       else:
113         self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
114   #
115
116   def __loadMPI(self, module_name):
117     print "Trying to load MPI module: %s..."%module_name,
118     try:
119       out, err = subprocess.Popen(["modulecmd", "python", "load", module_name], stdout=subprocess.PIPE).communicate()
120       exec out # define specific environment variables
121       print " OK"
122     except:
123       print " ** Failed **"
124       pass
125   #
126
127   def runSalome(self, args):
128     import os
129     # Run this module as a script, in order to use appropriate Python interpreter
130     # according to current path (initialized from context files).
131     mpi_module_option = "--with-mpi-module="
132     mpi_module = [x for x in args if x.startswith(mpi_module_option)]
133     if mpi_module:
134       mpi_module = mpi_module[0][len(mpi_module_option):]
135       self.__loadMPI(mpi_module)
136       args = [x for x in args if not x.startswith(mpi_module_option)]
137     else:
138       mpi_module = os.getenv("SALOME_MPI_MODULE_NAME", None)
139       if mpi_module:
140         self.__loadMPI(mpi_module)
141
142     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
143     env_copy = os.environ.copy()
144     proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeContext.py"), pickle.dumps(self), pickle.dumps(args)], shell=False, close_fds=True, env=env_copy)
145     msg = proc.communicate()
146     return msg, proc.returncode
147   #
148
149   """Append value to PATH environment variable"""
150   def addToPath(self, value):
151     self.addToVariable('PATH', value)
152   #
153
154   """Append value to LD_LIBRARY_PATH environment variable"""
155   def addToLdLibraryPath(self, value):
156     self.addToVariable('LD_LIBRARY_PATH', value)
157   #
158
159   """Append value to DYLD_LIBRARY_PATH environment variable"""
160   def addToDyldLibraryPath(self, value):
161     self.addToVariable('DYLD_LIBRARY_PATH', value)
162   #
163
164   """Append value to PYTHONPATH environment variable"""
165   def addToPythonPath(self, value):
166     self.addToVariable('PYTHONPATH', value)
167   #
168
169   """Set environment variable to value"""
170   def setVariable(self, name, value, overwrite=False):
171     env = os.getenv(name, '')
172     if env and not overwrite:
173       self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
174       return
175
176     if env:
177       self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
178
179     value = os.path.expandvars(value) # expand environment variables
180     self.getLogger().debug("Set environment variable: %s=%s", name, value)
181     os.environ[name] = value
182   #
183
184   """Unset environment variable"""
185   def unsetVariable(self, name):
186     if os.environ.has_key(name):
187       del os.environ[name]
188   #
189
190   """Append value to environment variable"""
191   def addToVariable(self, name, value, separator=os.pathsep):
192     if value == '':
193       return
194
195     value = os.path.expandvars(value) # expand environment variables
196     self.getLogger().debug("Add to %s: %s", name, value)
197     env = os.getenv(name, None)
198     if env is None:
199       os.environ[name] = value
200     else:
201       os.environ[name] = value + separator + env
202   #
203
204   ###################################
205   # This begins the private section #
206   ###################################
207
208   def __parseArguments(self, args):
209     if len(args) == 0 or args[0].startswith("-"):
210       return None, args
211
212     command = args[0]
213     options = args[1:]
214
215     availableCommands = {
216       'start'   : '_runAppli',
217       'context' : '_setContext',
218       'shell'   : '_runSession',
219       'connect' : '_runConsole',
220       'kill'    : '_kill',
221       'killall' : '_killAll',
222       'test'    : '_runTests',
223       'info'    : '_showInfo',
224       'help'    : '_usage',
225       'coffee'  : '_makeCoffee',
226       'car'     : '_getCar',
227       }
228
229     if not command in availableCommands.keys():
230       command = "start"
231       options = args
232
233     return availableCommands[command], options
234   #
235
236   """
237   Run SALOME!
238   Args consist in a mandatory command followed by optionnal parameters.
239   See usage for details on commands.
240   """
241   def _startSalome(self, args):
242     import os
243     import sys
244     try:
245       from setenv import add_path
246       absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
247       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
248       add_path(path, "PYTHONPATH")
249       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
250       add_path(path, "PYTHONPATH")
251
252     except:
253       pass
254
255     command, options = self.__parseArguments(args)
256     sys.argv = options
257
258     if command is None:
259       if args and args[0] in ["-h","--help","help"]:
260         usage()
261         sys.exit(0)
262       # try to default to "start" command
263       command = "_runAppli"
264
265     try:
266       res = getattr(self, command)(options) # run appropriate method
267       return res or (None, None)
268     except SystemExit, returncode:
269       if returncode != 0:
270         self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
271       sys.exit(returncode)
272     except StandardError:
273       self.getLogger().error("Unexpected error:")
274       import traceback
275       traceback.print_exc()
276       sys.exit(1)
277     except SalomeContextException, e:
278       self.getLogger().error(e)
279       sys.exit(1)
280   #
281
282   def __setContextFromConfigFile(self, filename, reserved=None):
283     if reserved is None:
284       reserved = []
285     try:
286       unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
287     except SalomeContextException, e:
288       msg = "%s"%e
289       file_dir = os.path.dirname(filename)
290       file_base = os.path.basename(filename)
291       base_no_ext, ext = os.path.splitext(file_base)
292       sh_file = os.path.join(file_dir, base_no_ext+'.sh')
293       if ext == ".cfg" and os.path.isfile(sh_file):
294         msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
295         temp = tempfile.NamedTemporaryFile(suffix='.cfg')
296         try:
297           convertEnvFileToConfigFile(sh_file, temp.name, reserved)
298           self.__setContextFromConfigFile(temp.name, reserved)
299           msg += "OK\n"
300           self.getLogger().warning(msg)
301           temp.close()
302           return
303         except (ConfigParser.ParsingError, ValueError) as e:
304           msg += "Invalid token found when parsing file: %s\n"%(sh_file)
305           self.getLogger().error(msg)
306           temp.close()
307           sys.exit(1)
308       else:
309         self.getLogger().error(msg)
310         sys.exit(1)
311
312     # unset variables
313     for var in unsetVars:
314       self.unsetVariable(var)
315
316     # set context
317     for reserved in reservedDict:
318       a = filter(None, reservedDict[reserved]) # remove empty elements
319       a = [ os.path.realpath(x) for x in a ]
320       reformattedVals = os.pathsep.join(a)
321       if reserved in ["INCLUDE", "LIBPATH"]:
322         self.addToVariable(reserved, reformattedVals, separator=' ')
323       else:
324         self.addToVariable(reserved, reformattedVals)
325       pass
326
327     for key,val in configVars:
328       self.setVariable(key, val, overwrite=True)
329       pass
330
331     pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
332     pythonpath = [ os.path.realpath(x) for x in pythonpath ]
333     sys.path[:0] = pythonpath
334   #
335
336   def _runAppli(self, args=[]):
337     # Initialize SALOME environment
338     sys.argv = ['runSalome'] + args
339     import setenv
340     setenv.main(True)
341
342     import runSalome
343     runSalome.runSalome()
344   #
345
346   def _setContext(self, args=None):
347     salome_context_set = os.getenv("SALOME_CONTEXT_SET")
348     if salome_context_set:
349       print "***"
350       print "*** SALOME context has already been set."
351       print "*** Enter 'exit' (only once!) to leave SALOME context."
352       print "***"
353       return
354
355     os.environ["SALOME_CONTEXT_SET"] = "yes"
356     print "***"
357     print "*** SALOME context is now set."
358     print "*** Enter 'exit' (only once!) to leave SALOME context."
359     print "***"
360
361     cmd = ["/bin/bash"]
362     proc = subprocess.Popen(cmd, shell=False, close_fds=True)
363     return proc.communicate()
364   #
365
366   def _runSession(self, args=[]):
367     sys.argv = ['runSession'] + args
368     import runSession
369     params, args = runSession.configureSession(args, exe="salome shell")
370
371     sys.argv = ['runSession'] + args
372     import setenv
373     setenv.main(True)
374
375     return runSession.runSession(params, args)
376   #
377
378   def _runConsole(self, args=[]):
379     # Initialize SALOME environment
380     sys.argv = ['runConsole'] + args
381     import setenv
382     setenv.main(True)
383
384     cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
385     proc = subprocess.Popen(cmd, shell=False, close_fds=True)
386     return proc.communicate()
387   #
388
389   def _kill(self, args=[]):
390     ports = args
391     if not ports:
392       print "Port number(s) not provided to command: salome kill <port(s)>"
393       return
394
395     from multiprocessing import Process
396     from killSalomeWithPort import killMyPort
397     import tempfile
398     for port in ports:
399       with tempfile.NamedTemporaryFile():
400         p = Process(target = killMyPort, args=(port,))
401         p.start()
402         p.join()
403     pass
404   #
405
406   def _killAll(self, unused=None):
407     try:
408       import PortManager # mandatory
409       from multiprocessing import Process
410       from killSalomeWithPort import killMyPort
411       ports = PortManager.getBusyPorts()
412
413       if ports:
414         import tempfile
415         for port in ports:
416           with tempfile.NamedTemporaryFile():
417             p = Process(target = killMyPort, args=(port,))
418             p.start()
419             p.join()
420     except ImportError:
421       from killSalome import killAllPorts
422       killAllPorts()
423       pass
424   #
425
426   def _runTests(self, args=[]):
427     sys.argv = ['runTests']
428     import setenv
429     setenv.main(True)
430
431     import runTests
432     return runTests.runTests(args, exe="salome test")
433   #
434
435   def _showInfo(self, unused=None):
436     print "Running with python", platform.python_version()
437     self._runAppli(["--version"])
438   #
439
440   def _usage(self, unused=None):
441     usage()
442   #
443
444   def _makeCoffee(self, unused=None):
445     print "                        ("
446     print "                          )     ("
447     print "                   ___...(-------)-....___"
448     print "               .-\"\"       )    (          \"\"-."
449     print "         .-\'``\'|-._             )         _.-|"
450     print "        /  .--.|   `\"\"---...........---\"\"`   |"
451     print "       /  /    |                             |"
452     print "       |  |    |                             |"
453     print "        \\  \\   |                             |"
454     print "         `\\ `\\ |                             |"
455     print "           `\\ `|            SALOME           |"
456     print "           _/ /\\            4 EVER           /"
457     print "          (__/  \\             <3            /"
458     print "       _..---\"\"` \\                         /`\"\"---.._"
459     print "    .-\'           \\                       /          \'-."
460     print "   :               `-.__             __.-\'              :"
461     print "   :                  ) \"\"---...---\"\" (                 :"
462     print "    \'._               `\"--...___...--\"`              _.\'"
463     print "      \\\"\"--..__                              __..--\"\"/"
464     print "       \'._     \"\"\"----.....______.....----\"\"\"     _.\'"
465     print "          `\"\"--..,,_____            _____,,..--\"\"`"
466     print "                        `\"\"\"----\"\"\"`"
467     print ""
468     print "                    SALOME is working for you; what else?"
469     print ""
470     sys.exit(0)
471   #
472
473   def _getCar(self, unused=None):
474     print "                                              _____________"
475     print "                                  ..---:::::::-----------. ::::;;."
476     print "                               .\'\"\"\"\"\"\"                  ;;   \\  \":."
477     print "                            .\'\'                          ;     \\   \"\\__."
478     print "                          .\'                            ;;      ;   \\\\\";"
479     print "                        .\'                              ;   _____;   \\\\/"
480     print "                      .\'                               :; ;\"     \\ ___:\'."
481     print "                    .\'--...........................    : =   ____:\"    \\ \\"
482     print "               ..-\"\"                               \"\"\"\'  o\"\"\"     ;     ; :"
483     print "          .--\"\"  .----- ..----...    _.-    --.  ..-\"     ;       ;     ; ;"
484     print "       .\"\"_-     \"--\"\"-----\'\"\"    _-\"        .-\"\"         ;        ;    .-."
485     print "    .\'  .\'   SALOME             .\"         .\"              ;       ;   /. |"
486     print "   /-./\'         4 EVER <3    .\"          /           _..  ;       ;   ;;;|"
487     print "  :  ;-.______               /       _________==.    /_  \\ ;       ;   ;;;;"
488     print "  ;  / |      \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\"          :    /\" \". |;       ; _; ;;;"
489     print " /\"-/  |                /   /                  /   /     ;|;      ;-\" | ;\';"
490     print ":-  :   \"\"\"----______  /   /              ____.   .  .\"\'. ;;   .-\"..T\"   ."
491     print "\'. \"  ___            \"\":   \'\"\"\"\"\"\"\"\"\"\"\"\"\"\"    .   ; ;    ;; ;.\" .\"   \'--\""
492     print " \",   __ \"\"\"  \"\"---... :- - - - - - - - - \' \'  ; ;  ;    ;;\"  .\""
493     print "  /. ;  \"\"\"---___                             ;  ; ;     ;|.\"\""
494     print " :  \":           \"\"\"----.    .-------.       ;   ; ;     ;:"
495     print "  \\  \'--__               \\   \\        \\     /    | ;     ;;"
496     print "   \'-..   \"\"\"\"---___      :   .______..\\ __/..-\"\"|  ;   ; ;"
497     print "       \"\"--..       \"\"\"--\"        m l s         .   \". . ;"
498     print "             \"\"------...                  ..--\"\"      \" :"
499     print "                        \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"    \\        /"
500     print "                                               \"------\""
501     print ""
502     print "                                Drive your simulation properly with SALOME!"
503     print ""
504     sys.exit(0)
505   #
506
507   # Add the following two methods since logger is not pickable
508   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
509   def __getstate__(self):
510     d = dict(self.__dict__)
511     if hasattr(self, '_logger'):
512       del d['_logger']
513     return d
514   #
515   def __setstate__(self, d):
516     self.__dict__.update(d) # I *think* this is a safe way to do it
517   #
518   # Excluding self._logger from pickle operation imply using the following method to access logger
519   def getLogger(self):
520     if not hasattr(self, '_logger'):
521       self._logger = logging.getLogger(__name__)
522       #self._logger.setLevel(logging.DEBUG)
523       #self._logger.setLevel(logging.WARNING)
524       self._logger.setLevel(logging.ERROR)
525     return self._logger
526   #
527
528 if __name__ == "__main__":
529   if len(sys.argv) == 3:
530     context = pickle.loads(sys.argv[1])
531     args = pickle.loads(sys.argv[2])
532
533     (out, err) = context._startSalome(args)
534     if out:
535       sys.stdout.write(out)
536     if err:
537       sys.stderr.write(err)
538   else:
539     usage()
540 #