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()
115 return msg, proc.returncode
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, returncode:
222 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
224 except StandardError:
225 self.getLogger().error("Unexpected error:")
227 traceback.print_exc()
229 except SalomeContextException, e:
230 self.getLogger().error(e)
234 def __setEnvironmentFromConfigFile(self, filename, reserved=[]):
236 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
237 except SalomeContextException, e:
239 file_dir = os.path.dirname(filename)
240 file_base = os.path.basename(filename)
241 base_no_ext, ext = os.path.splitext(file_base)
242 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
243 if ext == ".cfg" and os.path.isfile(sh_file):
244 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
245 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
247 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
248 self.__setEnvironmentFromConfigFile(temp.name, reserved)
250 self.getLogger().warning(msg)
253 except (ConfigParser.ParsingError, ValueError) as e:
254 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
255 self.getLogger().error(msg)
259 self.getLogger().error(msg)
263 for var in unsetVars:
264 self.unsetVariable(var)
267 for reserved in reservedDict:
268 a = filter(None, reservedDict[reserved]) # remove empty elements
269 reformattedVals = ':'.join(a)
270 self.addToVariable(reserved, reformattedVals)
273 for key,val in configVars:
274 self.setVariable(key, val, overwrite=True)
277 sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
280 def _runAppli(self, args=[]):
281 # Initialize SALOME environment
282 sys.argv = ['runSalome'] + args
287 runSalome.runSalome()
290 def _runSession(self, args=[]):
291 sys.argv = ['runSession'] + args
293 runSession.configureSession(args)
298 scriptArgs = getScriptsAndArgs(args)
299 command = formatScriptsAndArgs(scriptArgs)
302 if sys.platform == "win32":
304 command = command.split(sep)
309 cmd = cmd.strip().split(' ')
310 #proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
311 proc = subprocess.Popen(cmd)
312 (stdoutdata, stderrdata) = proc.communicate()
314 outmsg.append(stdoutdata)
316 errmsg.append(stderrdata)
318 if proc.returncode != 0:
319 errmsg.append("Error raised when executing command: %s\n"%save_cmd)
321 sys.stdout.write("".join(outmsg))
323 sys.stderr.write("".join(errmsg))
324 sys.exit(proc.returncode)
326 return ("".join(outmsg), "".join(errmsg))
328 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
329 cmd = ["/bin/bash", "--rcfile", absoluteAppliPath + "/.bashrc" ]
330 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
331 return proc.communicate()
334 def _runConsole(self, args=[]):
335 # Initialize SALOME environment
336 sys.argv = ['runConsole'] + args
340 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
341 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
342 return proc.communicate()
345 def _killAll(self, args=[]):
346 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
348 import PortManager # mandatory
349 from multiprocessing import Process
350 from killSalomeWithPort import killMyPort
351 ports = PortManager.getBusyPorts()
356 with tempfile.NamedTemporaryFile():
357 p = Process(target = killMyPort, args=(port,))
361 from killSalome import killAllPorts
367 def _showInfo(self, args=[]):
368 print "Running with python", platform.python_version()
369 self._runAppli(["--version"])
372 def _usage(self, unused=[]):
376 def _makeCoffee(self, args=[]):
379 print " ___...(-------)-....___"
380 print " .-\"\" ) ( \"\"-."
381 print " .-\'``\'|-._ ) _.-|"
382 print " / .--.| `\"\"---...........---\"\"` |"
390 print " _..---\"\"` \\ /`\"\"---.._"
391 print " .-\' \\ / \'-."
392 print " : `-.__ __.-\' :"
393 print " : ) \"\"---...---\"\" ( :"
394 print " \'._ `\"--...___...--\"` _.\'"
395 print " \\\"\"--..__ __..--\"\"/"
396 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
397 print " `\"\"--..,,_____ _____,,..--\"\"`"
398 print " `\"\"\"----\"\"\"`"
402 # Add the following two methods since logger is not pickable
403 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
404 def __getstate__(self):
405 d = dict(self.__dict__)
406 if hasattr(self, '_logger'):
410 def __setstate__(self, d):
411 self.__dict__.update(d) # I *think* this is a safe way to do it
413 # Excluding self._logger from pickle operation imply using the following method to access logger
415 if not hasattr(self, '_logger'):
416 self._logger = logging.getLogger(__name__)
417 #self._logger.setLevel(logging.DEBUG)
418 self._logger.setLevel(logging.WARNING)
423 if __name__ == "__main__":
424 if len(sys.argv) == 3:
425 context = pickle.loads(sys.argv[1])
426 args = pickle.loads(sys.argv[2])
428 (out, err) = context._startSalome(args)
430 sys.stdout.write(out)
432 sys.stderr.write(err)