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