Salome HOME
Windows 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     reserved=['PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']
62     for filename in configFileNames:
63       basename, extension = os.path.splitext(filename)
64       if extension == ".cfg":
65         self.__setEnvironmentFromConfigFile(filename, reserved)
66       elif extension == ".sh":
67         #new convert procedures, temporary could be use not to be automatically deleted
68         #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
69         temp = tempfile.NamedTemporaryFile(suffix='.cfg')
70         try:
71           convertEnvFileToConfigFile(filename, temp.name, reserved)
72           self.__setEnvironmentFromConfigFile(temp.name, reserved)
73         except ConfigParser.ParsingError, e:
74           self.getLogger().warning("Invalid token found when parsing file: %s\n"%(filename))
75           print e
76           print '\n'
77         finally:
78           # Automatically cleans up the file
79           temp.close()
80       else:
81         self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
82   #
83
84   def go(self, args):
85     # Run this module as a script, in order to use appropriate Python interpreter
86     # according to current path (initialized from environment files).
87     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
88     proc = subprocess.Popen(['python', absoluteAppliPath+'/bin/salome/salomeRunner.py', pickle.dumps(self),  pickle.dumps(args)], shell=False, close_fds=True)
89     proc.wait()
90   #
91
92   """Append value to PATH environment variable"""
93   def addToPath(self, value):
94     self.addToEnviron('PATH', value)
95   #
96
97   """Append value to LD_LIBRARY_PATH environment variable"""
98   def addToLdLibraryPath(self, value):
99     self.addToEnviron('LD_LIBRARY_PATH', value)
100   #
101
102   """Append value to PYTHONPATH environment variable"""
103   def addToPythonPath(self, value):
104     self.addToEnviron('PYTHONPATH', value)
105   #
106
107   """Set environment variable to value"""
108   def setEnviron(self, name, value, overwrite=False):
109     env = os.getenv(name, '')
110     if env and not overwrite:
111       self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
112       return
113
114     if env:
115       self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
116
117     value = os.path.expandvars(value) # expand environment variables
118     self.getLogger().debug("Set environment variable: %s=%s", name, value)
119     os.environ[name] = value
120   #
121
122   """Unset environment variable"""
123   def unsetEnviron(self, name):
124     if os.environ.has_key(name):
125       del os.environ[name]
126   #
127
128   """Append value to environment variable"""
129   def addToEnviron(self, name, value, separator=os.pathsep):
130     if value == '':
131       return
132
133     value = os.path.expandvars(value) # expand environment variables
134     self.getLogger().debug("Add to %s: %s", name, value)
135     env = os.getenv(name, None)
136     if env is None:
137       os.environ[name] = value
138     else:
139       os.environ[name] = value + separator + env
140   #
141
142   ###################################
143   # This begins the private section #
144   ###################################
145
146   def __parseArguments(self, args):
147     if len(args) == 0 or args[0].startswith("-"):
148       return None, args
149
150     command = args[0]
151     options = args[1:]
152
153     availableCommands = {
154       'start' :   '_runAppli',
155       'shell' :   '_runSession',
156       'connect' : '_runConsole',
157       'killall':  '_killAll',
158       'info':     '_showInfo',
159       'help':     '_usage',
160       'coffee' :  '_makeCoffee'
161       }
162
163     if not command in availableCommands.keys():
164       command = "start"
165       options = args
166
167     return availableCommands[command], options
168   #
169
170   """
171   Run SALOME!
172   Args consist in a mandatory command followed by optionnal parameters.
173   See usage for details on commands.
174   """
175   def _getStarted(self, args):
176     command, options = self.__parseArguments(args)
177     sys.argv = options
178
179     if command is None:
180       if args and args[0] in ["-h","--help","help"]:
181         usage()
182         sys.exit(0)
183       # try to default to "start" command
184       command = "_runAppli"
185
186     try:
187       res = getattr(self, command)(options) # run appropriate method
188       return res or (None, None)
189     except SystemExit, exc:
190       if exc==0:
191         sys.exit(0) #catch sys.exit(0) happy end no warning
192       if exc==1:
193         self.getLogger().warning("SystemExit 1 in method %s.", command)
194       sys.exit(1)
195     except StandardError:
196       self.getLogger().error("Unexpected error:")
197       import traceback
198       traceback.print_exc()
199       sys.exit(1)
200     except SalomeRunnerException, e:
201       self.getLogger().error(e)
202       sys.exit(1)
203   #
204
205   def __setEnvironmentFromConfigFile(self, filename, reserved=[]):
206     unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
207
208     # unset variables
209     for var in unsetVars:
210       self.unsetEnviron(var)
211
212     # set environment
213     for reserved in reservedDict:
214       a = filter(None, reservedDict[reserved]) # remove empty elements
215       reformattedVals = ':'.join(a)
216       self.addToEnviron(reserved, reformattedVals)
217       pass
218
219     for key,val in configVars:
220       self.setEnviron(key, val, overwrite=True)
221       pass
222
223     sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
224   #
225
226   def _runAppli(self, args=[]):
227     # Initialize SALOME environment
228     sys.argv = ['runSalome'] + args
229     import setenv
230     setenv.main(True)
231
232     import runSalome
233     runSalome.runSalome()
234   #
235
236   def _runSession(self, args=[]):
237     sys.argv = ['runSession'] + args
238     import runSession
239     runSession.configureSession(args)
240
241     import setenv
242     setenv.main(True)
243
244     scriptArgs = getScriptsAndArgs(args)
245     command = formatScriptsAndArgs(scriptArgs)
246     if command:
247       proc = subprocess.Popen(command, shell=True, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
248       return proc.communicate()
249     else:
250       absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
251       cmd = ["/bin/bash",  "--rcfile", absoluteAppliPath + "/.bashrc" ]
252       proc = subprocess.Popen(cmd, shell=False, close_fds=True)
253       proc.wait()
254   #
255
256   def _runConsole(self, args=[]):
257     # Initialize SALOME environment
258     sys.argv = ['runConsole'] + args
259     import setenv
260     setenv.main(True)
261
262     import runConsole
263     runConsole.connect()
264   #
265
266   def _killAll(self, args=[]):
267     from killSalome import killAllPorts
268     killAllPorts()
269   #
270
271   def _showInfo(self, args=[]):
272     print "Running with python", platform.python_version()
273     self._runAppli(["--version"])
274   #
275
276   def _usage(self, unused=[]):
277     usage()
278   #
279
280   def _makeCoffee(self, args=[]):
281     print "                        ("
282     print "                          )     ("
283     print "                   ___...(-------)-....___"
284     print "               .-\"\"       )    (          \"\"-."
285     print "         .-\'``\'|-._             )         _.-|"
286     print "        /  .--.|   `\"\"---...........---\"\"`   |"
287     print "       /  /    |                             |"
288     print "       |  |    |                             |"
289     print "        \\  \\   |                             |"
290     print "         `\\ `\\ |                             |"
291     print "           `\\ `|                             |"
292     print "           _/ /\\                             /"
293     print "          (__/  \\                           /"
294     print "       _..---\"\"` \\                         /`\"\"---.._"
295     print "    .-\'           \\                       /          \'-."
296     print "   :               `-.__             __.-\'              :"
297     print "   :                  ) \"\"---...---\"\" (                 :"
298     print "    \'._               `\"--...___...--\"`              _.\'"
299     print "      \\\"\"--..__                              __..--\"\"/"
300     print "       \'._     \"\"\"----.....______.....----\"\"\"     _.\'"
301     print "          `\"\"--..,,_____            _____,,..--\"\"`"
302     print "                        `\"\"\"----\"\"\"`"
303     sys.exit(0)
304   #
305
306   # Add the following two methods since logger is not pickable
307   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
308   def __getstate__(self):
309     d = dict(self.__dict__)
310     if hasattr(self, '_logger'):
311       del d['_logger']
312     return d
313   #
314   def __setstate__(self, d):
315     self.__dict__.update(d) # I *think* this is a safe way to do it
316   #
317   # Excluding self._logger from pickle operation imply using the following method to access logger
318   def getLogger(self):
319     if not hasattr(self, '_logger'):
320       self._logger = logging.getLogger(__name__)
321       #self._logger.setLevel(logging.DEBUG)
322       self._logger.setLevel(logging.ERROR)
323     return self._logger;
324   #
325
326 ###
327 import pickle
328 if __name__ == "__main__":
329   if len(sys.argv) == 3:
330     runner = pickle.loads(sys.argv[1])
331     args = pickle.loads(sys.argv[2])
332     (out, err) = runner._getStarted(args)
333     if out:
334       sys.stdout.write(out)
335     if err:
336       sys.stderr.write(err)
337   else:
338     usage()
339 #