Salome HOME
Fix
[modules/kernel.git] / bin / salomeRunner.py
1 import os
2 import sys
3 import logging
4 import ConfigParser
5
6 from parseConfigFile import parseConfigFile
7 from parseConfigFile import convertEnvFileToConfigFile
8
9 import tempfile
10 import pickle
11 import subprocess
12 import platform
13
14 from salomeLauncherUtils import SalomeRunnerException
15 from salomeLauncherUtils import getScriptsAndArgs, formatScriptsAndArgs
16
17 def usage():
18   #exeName = os.path.splitext(os.path.basename(__file__))[0]
19
20   msg = '''\
21 Usage: salome [command] [options] [--config=file1,...,filen]
22
23 Commands:
24     start         Launches SALOME virtual application [DEFAULT]
25     shell         Executes a script under SALOME application environment
26     connect       Connects a Python console to the active SALOME session
27     killall       Kill all SALOME running sessions
28     info          Display some information about SALOME
29     help          Show this message
30     coffee        Yes! SALOME can also make coffee!!"
31
32 Use salome start --help or salome shell --help
33 to show help on start and shell commands.
34 '''
35
36   print msg
37 #
38
39 """
40 The SalomeRunner class in an API to configure SALOME environment then
41 start SALOME using a single python command.
42
43 """
44 class SalomeRunner:
45   """
46   Initialize environment from a list of configuration files
47   identified by their names.
48   These files should be in appropriate (new .cfg) format.
49   However you can give old .sh environment files; in this case,
50   the SalomeRunner class will try to automatically convert them
51   to .cfg format before setting the environment.
52   """
53   def __init__(self, configFileNames=[]):
54     #it could be None explicitely (if user use multiples setEnviron...for standalone)
55     if configFileNames==None:
56        return
57
58     if len(configFileNames) == 0:
59       raise SalomeRunnerException("No configuration files given")
60
61     for filename in configFileNames:
62       basename, extension = os.path.splitext(filename)
63       if extension == ".cfg":
64         self.__setEnvironmentFromConfigFile(filename)
65       elif extension == ".sh":
66         #new convert procedures, temporary could be use not to be automatically deleted
67         #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
68         temp = tempfile.NamedTemporaryFile(suffix='.cfg')
69         try:
70           convertEnvFileToConfigFile(filename, temp.name)
71           self.__setEnvironmentFromConfigFile(temp.name)
72         except ConfigParser.ParsingError, e:
73           self.getLogger().warning("Invalid token found when parsing file: %s\n"%(filename))
74           print e
75           print '\n'
76         finally:
77           # Automatically cleans up the file
78           temp.close()
79       else:
80         self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
81   #
82
83   def go(self, args):
84     # Run this module as a script, in order to use appropriate Python interpreter
85     # according to current path (initialized from environment files).
86     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
87     proc = subprocess.Popen(['python', absoluteAppliPath+'/bin/salome/salomeRunner.py', pickle.dumps(self),  pickle.dumps(args)], shell=False, close_fds=True)
88     proc.wait()
89   #
90
91   """Append value to PATH environment variable"""
92   def addToPath(self, value):
93     self.__addToReserved('PATH', value)
94   #
95
96   """Append value to LD_LIBRARY_PATH environment variable"""
97   def addToLdLibraryPath(self, value):
98     self.__addToReserved('LD_LIBRARY_PATH', value)
99   #
100
101   """Append value to PYTHONPATH environment variable"""
102   def addToPythonPath(self, value):
103     self.__addToReserved('PYTHONPATH', value)
104   #
105
106   """Append value to TCLLIBPATH environment variable"""
107   def addToTclLibPath(self, value):
108     self.__addToReservedTclTk('TCLLIBPATH', value)
109   #
110
111   """Append value to TKLIBPATH environment variable"""
112   def addToTkLibPath(self, value):
113     self.__addToReservedTclTk('TKLIBPATH', value)
114   #
115
116   """Set environment variable to value"""
117   def setEnviron(self, name, value, overwrite=False):
118     env = os.getenv(name, '')
119     if env and not overwrite:
120       self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
121       return
122
123     if env:
124       self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
125
126     value = os.path.expandvars(value) # expand environment variables
127     self.getLogger().debug("Set environment variable: %s=%s", name, value)
128     os.environ[name] = value
129   #
130
131   """Unset environment variable"""
132   def unsetEnviron(self, name):
133     if os.environ.has_key(name):
134       del os.environ[name]
135   #
136
137   ###################################
138   # This begins the private section #
139   ###################################
140
141   def __parseArguments(self, args):
142     if len(args) == 0 or args[0].startswith("-"):
143       return None, args
144
145     command = args[0]
146     options = args[1:]
147
148     availableCommands = {
149       'start' :   '_runAppli',
150       'shell' :   '_runSession',
151       'connect' : '_runConsole',
152       'killall':  '_killAll',
153       'info':     '_showInfo',
154       'help':     '_usage',
155       'coffee' :  '_makeCoffee'
156       }
157
158     if not command in availableCommands.keys():
159       command = "start"
160       options = args
161
162     return availableCommands[command], options
163   #
164
165   """
166   Run SALOME!
167   Args consist in a mandatory command followed by optionnal parameters.
168   See usage for details on commands.
169   """
170   def _getStarted(self, args):
171     command, options = self.__parseArguments(args)
172     sys.argv = options
173
174     if command is None:
175       if args and args[0] in ["-h","--help","help"]:
176         usage()
177         sys.exit(0)
178       # try to default to "start" command
179       command = "_runAppli"
180
181     try:
182       res = getattr(self, command)(options) # run appropriate method
183       return res or (None, None)
184     except SystemExit, exc:
185       if exc==0:
186         sys.exit(0) #catch sys.exit(0) happy end no warning
187       if exc==1:
188         self.getLogger().warning("SystemExit 1 in method %s.", command)
189       sys.exit(1)
190     except StandardError:
191       self.getLogger().error("Unexpected error:")
192       import traceback
193       traceback.print_exc()
194       sys.exit(1)
195     except SalomeRunnerException, e:
196       self.getLogger().error(e)
197       sys.exit(1)
198   #
199
200   def __setEnvironmentFromConfigFile(self, filename):
201     unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved=['PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH'])
202
203     # unset variables
204     for var in unsetVars:
205       self.unsetEnviron(var)
206
207     # set environment
208     for reserved in reservedDict:
209       a = filter(None, reservedDict[reserved]) # remove empty elements
210       reformattedVals = ':'.join(a)
211       self.__addToReserved(reserved, reformattedVals)
212       pass
213
214     for key,val in configVars:
215       self.setEnviron(key, val, overwrite=True)
216       pass
217
218     sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
219   #
220
221   def __addToReserved(self, name, value):
222     if value == '':
223       return
224
225     value = os.path.expandvars(value) # expand environment variables
226     self.getLogger().debug("Add to %s: %s", name, value)
227     env = os.getenv(name, None)
228     if env is None:
229       os.environ[name] = value
230     else:
231       os.environ[name] = value + os.pathsep + env
232   #
233
234   def __addToReservedTclTk(self, name, value):
235     if value == '':
236       return
237
238     value = os.path.expandvars(value) # expand environment variables
239     self.getLogger().debug("Add to %s: %s", name, value)
240     env = os.getenv(name, None)
241     #http://computer-programming-forum.com/57-tcl/1dfddc136afccb94.htm
242     #Tcl treats the contents of that variable as a list. Be happy, for you can now use drive letters on windows.
243     if env is None:
244       os.environ[name] = value
245     else:
246       os.environ[name] = value + " " + env #explicitely whitespace
247   #
248
249   def _runAppli(self, args=[]):
250     # Initialize SALOME environment
251     sys.argv = ['runSalome'] + args
252     import setenv
253     setenv.main(True)
254
255     import runSalome
256     runSalome.runSalome()
257   #
258
259   def _runSession(self, args=[]):
260     sys.argv = ['runSession'] + args
261     import runSession
262     runSession.configureSession(args)
263
264     import setenv
265     setenv.main(True)
266
267     scriptArgs = getScriptsAndArgs(args)
268     command = formatScriptsAndArgs(scriptArgs)
269     if command:
270       proc = subprocess.Popen(command, shell=True, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
271       return proc.communicate()
272     else:
273       absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
274       cmd = ["/bin/bash",  "--rcfile", absoluteAppliPath + "/.bashrc" ]
275       proc = subprocess.Popen(cmd, shell=False, close_fds=True)
276       proc.wait()
277   #
278
279   def _runConsole(self, args=[]):
280     # Initialize SALOME environment
281     sys.argv = ['runConsole'] + args
282     import setenv
283     setenv.main(True)
284
285     import runConsole
286     runConsole.connect()
287   #
288
289   def _killAll(self, args=[]):
290     from killSalome import killAllPorts
291     killAllPorts()
292   #
293
294   def _showInfo(self, args=[]):
295     print "Running with python", platform.python_version()
296     self._runAppli(["--version"])
297   #
298
299   def _usage(self, unused=[]):
300     usage()
301   #
302
303   def _makeCoffee(self, args=[]):
304     print "                        ("
305     print "                          )     ("
306     print "                   ___...(-------)-....___"
307     print "               .-\"\"       )    (          \"\"-."
308     print "         .-\'``\'|-._             )         _.-|"
309     print "        /  .--.|   `\"\"---...........---\"\"`   |"
310     print "       /  /    |                             |"
311     print "       |  |    |                             |"
312     print "        \\  \\   |                             |"
313     print "         `\\ `\\ |                             |"
314     print "           `\\ `|                             |"
315     print "           _/ /\\                             /"
316     print "          (__/  \\                           /"
317     print "       _..---\"\"` \\                         /`\"\"---.._"
318     print "    .-\'           \\                       /          \'-."
319     print "   :               `-.__             __.-\'              :"
320     print "   :                  ) \"\"---...---\"\" (                 :"
321     print "    \'._               `\"--...___...--\"`              _.\'"
322     print "      \\\"\"--..__                              __..--\"\"/"
323     print "       \'._     \"\"\"----.....______.....----\"\"\"     _.\'"
324     print "          `\"\"--..,,_____            _____,,..--\"\"`"
325     print "                        `\"\"\"----\"\"\"`"
326     sys.exit(0)
327   #
328
329   # Add the following two methods since logger is not pickable
330   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
331   def __getstate__(self):
332     d = dict(self.__dict__)
333     if hasattr(self, '_logger'):
334       del d['_logger']
335     return d
336   #
337   def __setstate__(self, d):
338     self.__dict__.update(d) # I *think* this is a safe way to do it
339   #
340   # Excluding self._logger from pickle operation imply using the following method to access logger
341   def getLogger(self):
342     if not hasattr(self, '_logger'):
343       self._logger = logging.getLogger(__name__)
344       #self._logger.setLevel(logging.DEBUG)
345       self._logger.setLevel(logging.ERROR)
346     return self._logger;
347   #
348
349 ###
350 import pickle
351 if __name__ == "__main__":
352   if len(sys.argv) == 3:
353     runner = pickle.loads(sys.argv[1])
354     args = pickle.loads(sys.argv[2])
355     (out, err) = runner._getStarted(args)
356     if out:
357       sys.stdout.write(out)
358     if err:
359       sys.stderr.write(err)
360   else:
361     usage()
362 #