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