]> SALOME platform Git repositories - modules/kernel.git/blob - bin/salomeContext.py
Salome HOME
add option to select mpi module
[modules/kernel.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     shell         Initializes SALOME environment, and executes scripts passed
45                   as command arguments
46     connect       Connects a Python console to the active SALOME session
47     killall       Kill all SALOME running sessions for current user
48     info          Display some information about SALOME
49     help          Show this message
50     coffee        Yes! SALOME can also make coffee!!
51
52 If no command is given, default to start.
53
54 Command options:
55 ================
56     Use salome <command> --help to show help on command ; available for start
57     and shell commands.
58
59 --config=<file,folder,...>
60 ==========================
61     Initialize SALOME environment from a list of context files and/or a list
62     of folders containing context files. The list is comma-separated, whithout
63     any blank characters.
64 '''
65
66   print msg
67 #
68
69 """
70 The SalomeContext class in an API to configure SALOME environment then
71 start SALOME using a single python command.
72
73 """
74 class SalomeContext:
75   """
76   Initialize environment from a list of configuration files
77   identified by their names.
78   These files should be in appropriate (new .cfg) format.
79   However you can give old .sh environment files; in this case,
80   the SalomeContext class will try to automatically convert them
81   to .cfg format before setting the environment.
82   """
83   def __init__(self, configFileNames=0):
84     #it could be None explicitely (if user use multiples setVariable...for standalone)
85     if configFileNames is None:
86        return
87     configFileNames = configFileNames or []
88     if len(configFileNames) == 0:
89       raise SalomeContextException("No configuration files given")
90
91     reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']
92     for filename in configFileNames:
93       basename, extension = os.path.splitext(filename)
94       if extension == ".cfg":
95         self.__setEnvironmentFromConfigFile(filename, reserved)
96       elif extension == ".sh":
97         #new convert procedures, temporary could be use not to be automatically deleted
98         #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
99         temp = tempfile.NamedTemporaryFile(suffix='.cfg')
100         try:
101           convertEnvFileToConfigFile(filename, temp.name, reserved)
102           self.__setEnvironmentFromConfigFile(temp.name, reserved)
103           temp.close()
104         except (ConfigParser.ParsingError, ValueError) as e:
105           self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
106           temp.close()
107           sys.exit(1)
108       else:
109         self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
110   #
111
112   def runSalome(self, args):
113     # Run this module as a script, in order to use appropriate Python interpreter
114     # according to current path (initialized from environment files).
115 #    kill = False
116 #    for e in args:
117 #      if "--shutdown-server" in e:
118 #        kill = True
119 #        args.remove(e)
120
121     import os
122     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
123     env_copy = os.environ.copy()
124     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)
125     msg = proc.communicate()
126  #   if kill:
127  #     self._killAll(args)
128     return msg, proc.returncode
129   #
130
131   """Append value to PATH environment variable"""
132   def addToPath(self, value):
133     self.addToVariable('PATH', value)
134   #
135
136   """Append value to LD_LIBRARY_PATH environment variable"""
137   def addToLdLibraryPath(self, value):
138     self.addToVariable('LD_LIBRARY_PATH', value)
139   #
140
141   """Append value to DYLD_LIBRARY_PATH environment variable"""
142   def addToDyldLibraryPath(self, value):
143     self.addToVariable('DYLD_LIBRARY_PATH', value)
144   #
145
146   """Append value to PYTHONPATH environment variable"""
147   def addToPythonPath(self, value):
148     self.addToVariable('PYTHONPATH', value)
149   #
150
151   """Set environment variable to value"""
152   def setVariable(self, name, value, overwrite=False):
153     env = os.getenv(name, '')
154     if env and not overwrite:
155       self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
156       return
157
158     if env:
159       self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
160
161     value = os.path.expandvars(value) # expand environment variables
162     self.getLogger().debug("Set environment variable: %s=%s", name, value)
163     os.environ[name] = value
164   #
165
166   """Unset environment variable"""
167   def unsetVariable(self, name):
168     if os.environ.has_key(name):
169       del os.environ[name]
170   #
171
172   """Append value to environment variable"""
173   def addToVariable(self, name, value, separator=os.pathsep):
174     if value == '':
175       return
176
177     value = os.path.expandvars(value) # expand environment variables
178     self.getLogger().debug("Add to %s: %s", name, value)
179     env = os.getenv(name, None)
180     if env is None:
181       os.environ[name] = value
182     else:
183       os.environ[name] = value + separator + env
184   #
185
186   ###################################
187   # This begins the private section #
188   ###################################
189
190   def __parseArguments(self, args):
191     if len(args) == 0 or args[0].startswith("-"):
192       return None, args
193
194     command = args[0]
195     options = args[1:]
196
197     availableCommands = {
198       'start' :   '_runAppli',
199       'shell' :   '_runSession',
200       'connect' : '_runConsole',
201       'killall':  '_killAll',
202       'info':     '_showInfo',
203       'help':     '_usage',
204       'coffee' :  '_makeCoffee'
205       }
206
207     if not command in availableCommands.keys():
208       command = "start"
209       options = args
210
211     return availableCommands[command], options
212   #
213
214   """
215   Run SALOME!
216   Args consist in a mandatory command followed by optionnal parameters.
217   See usage for details on commands.
218   """
219   def _startSalome(self, args):
220     try:
221       import os
222       absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
223       import sys
224       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
225       if not path in sys.path:
226         sys.path[:0] = [path]
227     except:
228       pass
229
230     command, options = self.__parseArguments(args)
231     sys.argv = options
232
233     if command is None:
234       if args and args[0] in ["-h","--help","help"]:
235         usage()
236         sys.exit(0)
237       # try to default to "start" command
238       command = "_runAppli"
239
240     mpi_module_option = "--with_mpi_module="
241     mpi_module = [x for x in args if x.startswith(mpi_module_option)]
242     if mpi_module:
243       mpi_module = mpi_module[0][len(mpi_module_option):]
244       print "Trying to load MPI module: %s..."%mpi_module,
245       import subprocess
246       try:
247         subprocess.call(["module", "load", mpi_module])
248         print " OK"
249       except:
250         print " ** Failed **"
251         pass
252       options = [x for x in options if not x.startswith(mpi_module_option)]
253       pass
254
255     try:
256       res = getattr(self, command)(options) # run appropriate method
257       return res or (None, None)
258     except SystemExit, returncode:
259       if returncode != 0:
260         self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
261       sys.exit(returncode)
262     except StandardError:
263       self.getLogger().error("Unexpected error:")
264       import traceback
265       traceback.print_exc()
266       sys.exit(1)
267     except SalomeContextException, e:
268       self.getLogger().error(e)
269       sys.exit(1)
270   #
271
272   def __setEnvironmentFromConfigFile(self, filename, reserved=None):
273     if reserved is None:
274       reserved = []
275     try:
276       unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
277     except SalomeContextException, e:
278       msg = "%s"%e
279       file_dir = os.path.dirname(filename)
280       file_base = os.path.basename(filename)
281       base_no_ext, ext = os.path.splitext(file_base)
282       sh_file = os.path.join(file_dir, base_no_ext+'.sh')
283       if ext == ".cfg" and os.path.isfile(sh_file):
284         msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
285         temp = tempfile.NamedTemporaryFile(suffix='.cfg')
286         try:
287           convertEnvFileToConfigFile(sh_file, temp.name, reserved)
288           self.__setEnvironmentFromConfigFile(temp.name, reserved)
289           msg += "OK\n"
290           self.getLogger().warning(msg)
291           temp.close()
292           return
293         except (ConfigParser.ParsingError, ValueError) as e:
294           msg += "Invalid token found when parsing file: %s\n"%(sh_file)
295           self.getLogger().error(msg)
296           temp.close()
297           sys.exit(1)
298       else:
299         self.getLogger().error(msg)
300         sys.exit(1)
301
302     # unset variables
303     for var in unsetVars:
304       self.unsetVariable(var)
305
306     # set environment
307     for reserved in reservedDict:
308       a = filter(None, reservedDict[reserved]) # remove empty elements
309       a = [ os.path.realpath(x) for x in a ]
310       reformattedVals = os.pathsep.join(a)
311       self.addToVariable(reserved, reformattedVals)
312       pass
313
314     for key,val in configVars:
315       self.setVariable(key, val, overwrite=True)
316       pass
317
318     pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
319     pythonpath = [ os.path.realpath(x) for x in pythonpath ]
320     sys.path[:0] = pythonpath
321   #
322
323   def _runAppli(self, args=None):
324     if args is None:
325       args = []
326     # Initialize SALOME environment
327     sys.argv = ['runSalome'] + args
328     import setenv
329     setenv.main(True)
330
331     import runSalome
332     runSalome.runSalome()
333   #
334
335   def _runSession(self, args=None):
336     if args is None:
337       args = []
338     sys.argv = ['runSession'] + args
339     import runSession
340     params, args = runSession.configureSession(args, exe="salome shell")
341
342     sys.argv = ['runSession'] + args
343     import setenv
344     setenv.main(True)
345
346     return runSession.runSession(params, args)
347   #
348
349   def _runConsole(self, args=None):
350     if args is None:
351       args = []
352     # Initialize SALOME environment
353     sys.argv = ['runConsole'] + args
354     import setenv
355     setenv.main(True)
356
357     cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
358     proc = subprocess.Popen(cmd, shell=False, close_fds=True)
359     return proc.communicate()
360   #
361
362   def _killAll(self, args=None):
363     if args is None:
364       args = []
365     try:
366       import PortManager # mandatory
367       from multiprocessing import Process
368       from killSalomeWithPort import killMyPort
369       ports = PortManager.getBusyPorts()
370
371       if ports:
372         import tempfile
373         for port in ports:
374           with tempfile.NamedTemporaryFile():
375             p = Process(target = killMyPort, args=(port,))
376             p.start()
377             p.join()
378     except ImportError:
379       from killSalome import killAllPorts
380       killAllPorts()
381       pass
382
383   #
384
385   def _showInfo(self, args=None):
386     print "Running with python", platform.python_version()
387     self._runAppli(["--version"])
388   #
389
390   def _usage(self, unused=None):
391     usage()
392   #
393
394   def _makeCoffee(self, args=None):
395     print "                        ("
396     print "                          )     ("
397     print "                   ___...(-------)-....___"
398     print "               .-\"\"       )    (          \"\"-."
399     print "         .-\'``\'|-._             )         _.-|"
400     print "        /  .--.|   `\"\"---...........---\"\"`   |"
401     print "       /  /    |                             |"
402     print "       |  |    |                             |"
403     print "        \\  \\   |                             |"
404     print "         `\\ `\\ |                             |"
405     print "           `\\ `|                             |"
406     print "           _/ /\\                             /"
407     print "          (__/  \\                           /"
408     print "       _..---\"\"` \\                         /`\"\"---.._"
409     print "    .-\'           \\                       /          \'-."
410     print "   :               `-.__             __.-\'              :"
411     print "   :                  ) \"\"---...---\"\" (                 :"
412     print "    \'._               `\"--...___...--\"`              _.\'"
413     print "      \\\"\"--..__                              __..--\"\"/"
414     print "       \'._     \"\"\"----.....______.....----\"\"\"     _.\'"
415     print "          `\"\"--..,,_____            _____,,..--\"\"`"
416     print "                        `\"\"\"----\"\"\"`"
417     sys.exit(0)
418   #
419
420   # Add the following two methods since logger is not pickable
421   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
422   def __getstate__(self):
423     d = dict(self.__dict__)
424     if hasattr(self, '_logger'):
425       del d['_logger']
426     return d
427   #
428   def __setstate__(self, d):
429     self.__dict__.update(d) # I *think* this is a safe way to do it
430   #
431   # Excluding self._logger from pickle operation imply using the following method to access logger
432   def getLogger(self):
433     if not hasattr(self, '_logger'):
434       self._logger = logging.getLogger(__name__)
435       #self._logger.setLevel(logging.DEBUG)
436       #self._logger.setLevel(logging.WARNING)
437       self._logger.setLevel(logging.ERROR)
438     return self._logger
439   #
440
441 if __name__ == "__main__":
442   if len(sys.argv) == 3:
443     context = pickle.loads(sys.argv[1])
444     args = pickle.loads(sys.argv[2])
445
446     (out, err) = context._startSalome(args)
447     if out:
448       sys.stdout.write(out)
449     if err:
450       sys.stderr.write(err)
451   else:
452     usage()
453 #