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