1 # Copyright (C) 2013-2014 CEA/DEN, EDF R&D, OPEN CASCADE
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.
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.
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
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
25 from parseConfigFile import parseConfigFile
26 from parseConfigFile import convertEnvFileToConfigFile
33 from salomeContextUtils import SalomeContextException
34 from salomeContextUtils import getScriptsAndArgs, formatScriptsAndArgs
37 #exeName = os.path.splitext(os.path.basename(__file__))[0]
40 Usage: salome [command] [options] [--config=file1,...,filen]
43 start Launches SALOME virtual application [DEFAULT]
44 shell Executes a script under SALOME application environment
45 connect Connects a Python console to the active SALOME session
46 killall Kill all SALOME running sessions
47 info Display some information about SALOME
48 help Show this message
49 coffee Yes! SALOME can also make coffee!!"
51 Use salome start --help or salome shell --help
52 to show help on start and shell commands.
59 The SalomeContext class in an API to configure SALOME environment then
60 start SALOME using a single python command.
65 Initialize environment from a list of configuration files
66 identified by their names.
67 These files should be in appropriate (new .cfg) format.
68 However you can give old .sh environment files; in this case,
69 the SalomeContext class will try to automatically convert them
70 to .cfg format before setting the environment.
72 def __init__(self, configFileNames=[]):
73 #it could be None explicitely (if user use multiples setVariable...for standalone)
74 if configFileNames==None:
77 if len(configFileNames) == 0:
78 raise SalomeContextException("No configuration files given")
80 reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']
81 for filename in configFileNames:
82 basename, extension = os.path.splitext(filename)
83 if extension == ".cfg":
84 self.__setEnvironmentFromConfigFile(filename, reserved)
85 elif extension == ".sh":
86 #new convert procedures, temporary could be use not to be automatically deleted
87 #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
88 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
90 convertEnvFileToConfigFile(filename, temp.name, reserved)
91 self.__setEnvironmentFromConfigFile(temp.name, reserved)
93 except (ConfigParser.ParsingError, ValueError) as e:
94 self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
98 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
101 def runSalome(self, args):
102 # Run this module as a script, in order to use appropriate Python interpreter
103 # according to current path (initialized from environment files).
106 if "--shutdown-server" in e:
110 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
111 proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeContext.py"), pickle.dumps(self), pickle.dumps(args)], shell=False, close_fds=True)
112 msg = proc.communicate()
118 """Append value to PATH environment variable"""
119 def addToPath(self, value):
120 self.addToVariable('PATH', value)
123 """Append value to LD_LIBRARY_PATH environment variable"""
124 def addToLdLibraryPath(self, value):
125 self.addToVariable('LD_LIBRARY_PATH', value)
128 """Append value to DYLD_LIBRARY_PATH environment variable"""
129 def addToDyldLibraryPath(self, value):
130 self.addToVariable('DYLD_LIBRARY_PATH', value)
133 """Append value to PYTHONPATH environment variable"""
134 def addToPythonPath(self, value):
135 self.addToVariable('PYTHONPATH', value)
138 """Set environment variable to value"""
139 def setVariable(self, name, value, overwrite=False):
140 env = os.getenv(name, '')
141 if env and not overwrite:
142 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
146 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
148 value = os.path.expandvars(value) # expand environment variables
149 self.getLogger().debug("Set environment variable: %s=%s", name, value)
150 os.environ[name] = value
153 """Unset environment variable"""
154 def unsetVariable(self, name):
155 if os.environ.has_key(name):
159 """Append value to environment variable"""
160 def addToVariable(self, name, value, separator=os.pathsep):
164 value = os.path.expandvars(value) # expand environment variables
165 self.getLogger().debug("Add to %s: %s", name, value)
166 env = os.getenv(name, None)
168 os.environ[name] = value
170 os.environ[name] = value + separator + env
173 ###################################
174 # This begins the private section #
175 ###################################
177 def __parseArguments(self, args):
178 if len(args) == 0 or args[0].startswith("-"):
184 availableCommands = {
185 'start' : '_runAppli',
186 'shell' : '_runSession',
187 'connect' : '_runConsole',
188 'killall': '_killAll',
191 'coffee' : '_makeCoffee'
194 if not command in availableCommands.keys():
198 return availableCommands[command], options
203 Args consist in a mandatory command followed by optionnal parameters.
204 See usage for details on commands.
206 def _startSalome(self, args):
207 command, options = self.__parseArguments(args)
211 if args and args[0] in ["-h","--help","help"]:
214 # try to default to "start" command
215 command = "_runAppli"
218 res = getattr(self, command)(options) # run appropriate method
219 return res or (None, None)
220 except SystemExit, exc:
222 sys.exit(0) #catch sys.exit(0) happy end no warning
224 self.getLogger().warning("SystemExit 1 in method %s.", command)
226 except StandardError:
227 self.getLogger().error("Unexpected error:")
229 traceback.print_exc()
231 except SalomeContextException, e:
232 self.getLogger().error(e)
236 def __setEnvironmentFromConfigFile(self, filename, reserved=[]):
238 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
239 except SalomeContextException, e:
241 file_dir = os.path.dirname(filename)
242 file_base = os.path.basename(filename)
243 base_no_ext, ext = os.path.splitext(file_base)
244 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
245 if ext == ".cfg" and os.path.isfile(sh_file):
246 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
247 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
249 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
250 self.__setEnvironmentFromConfigFile(temp.name, reserved)
252 self.getLogger().warning(msg)
255 except (ConfigParser.ParsingError, ValueError) as e:
256 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
257 self.getLogger().error(msg)
261 self.getLogger().error(msg)
265 for var in unsetVars:
266 self.unsetVariable(var)
269 for reserved in reservedDict:
270 a = filter(None, reservedDict[reserved]) # remove empty elements
271 reformattedVals = ':'.join(a)
272 self.addToVariable(reserved, reformattedVals)
275 for key,val in configVars:
276 self.setVariable(key, val, overwrite=True)
279 sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
282 def _runAppli(self, args=[]):
283 # Initialize SALOME environment
284 sys.argv = ['runSalome'] + args
289 runSalome.runSalome()
292 def _runSession(self, args=[]):
293 sys.argv = ['runSession'] + args
295 runSession.configureSession(args)
300 scriptArgs = getScriptsAndArgs(args)
301 command = formatScriptsAndArgs(scriptArgs)
304 if sys.platform == "win32":
306 command = command.split(sep)
310 cmd = cmd.strip().split(' ')
311 #proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
312 proc = subprocess.Popen(cmd)
313 (stdoutdata, stderrdata) = proc.communicate()
314 if stdoutdata or stderrdata:
315 outmsg.append(stdoutdata)
316 errmsg.append(stderrdata)
318 return ("".join(outmsg), "".join(errmsg))
320 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
321 cmd = ["/bin/bash", "--rcfile", absoluteAppliPath + "/.bashrc" ]
322 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
323 return proc.communicate()
326 def _runConsole(self, args=[]):
327 # Initialize SALOME environment
328 sys.argv = ['runConsole'] + args
332 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
333 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
334 return proc.communicate()
337 def _killAll(self, args=[]):
338 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
340 import PortManager # mandatory
341 from multiprocessing import Process
342 from killSalomeWithPort import killMyPort
343 ports = PortManager.getBusyPorts()
348 with tempfile.NamedTemporaryFile():
349 p = Process(target = killMyPort, args=(port,))
353 p = Process(target = killMyPort, args=(2809,))
357 from killSalome import killAllPorts
363 def _showInfo(self, args=[]):
364 print "Running with python", platform.python_version()
365 self._runAppli(["--version"])
368 def _usage(self, unused=[]):
372 def _makeCoffee(self, args=[]):
375 print " ___...(-------)-....___"
376 print " .-\"\" ) ( \"\"-."
377 print " .-\'``\'|-._ ) _.-|"
378 print " / .--.| `\"\"---...........---\"\"` |"
386 print " _..---\"\"` \\ /`\"\"---.._"
387 print " .-\' \\ / \'-."
388 print " : `-.__ __.-\' :"
389 print " : ) \"\"---...---\"\" ( :"
390 print " \'._ `\"--...___...--\"` _.\'"
391 print " \\\"\"--..__ __..--\"\"/"
392 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
393 print " `\"\"--..,,_____ _____,,..--\"\"`"
394 print " `\"\"\"----\"\"\"`"
398 # Add the following two methods since logger is not pickable
399 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
400 def __getstate__(self):
401 d = dict(self.__dict__)
402 if hasattr(self, '_logger'):
406 def __setstate__(self, d):
407 self.__dict__.update(d) # I *think* this is a safe way to do it
409 # Excluding self._logger from pickle operation imply using the following method to access logger
411 if not hasattr(self, '_logger'):
412 self._logger = logging.getLogger(__name__)
413 #self._logger.setLevel(logging.DEBUG)
414 self._logger.setLevel(logging.WARNING)
420 if __name__ == "__main__":
421 if len(sys.argv) == 3:
422 context = pickle.loads(sys.argv[1])
423 args = pickle.loads(sys.argv[2])
424 (out, err) = context._startSalome(args)
426 sys.stdout.write(out)
428 sys.stderr.write(err)