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