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', '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)
92 except ConfigParser.ParsingError, e:
93 self.getLogger().warning("Invalid token found when parsing file: %s\n"%(filename))
97 # Automatically cleans up the file
100 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
103 def runSalome(self, args):
104 # Run this module as a script, in order to use appropriate Python interpreter
105 # according to current path (initialized from environment files).
108 if "--shutdown-server" in e:
112 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
113 proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeContext.py"), pickle.dumps(self), pickle.dumps(args)], shell=False, close_fds=True)
119 """Append value to PATH environment variable"""
120 def addToPath(self, value):
121 self.addToVariable('PATH', value)
124 """Append value to LD_LIBRARY_PATH environment variable"""
125 def addToLdLibraryPath(self, value):
126 self.addToVariable('LD_LIBRARY_PATH', value)
129 """Append value to PYTHONPATH environment variable"""
130 def addToPythonPath(self, value):
131 self.addToVariable('PYTHONPATH', value)
134 """Set environment variable to value"""
135 def setVariable(self, name, value, overwrite=False):
136 env = os.getenv(name, '')
137 if env and not overwrite:
138 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
142 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
144 value = os.path.expandvars(value) # expand environment variables
145 self.getLogger().debug("Set environment variable: %s=%s", name, value)
146 os.environ[name] = value
149 """Unset environment variable"""
150 def unsetVariable(self, name):
151 if os.environ.has_key(name):
155 """Append value to environment variable"""
156 def addToVariable(self, name, value, separator=os.pathsep):
160 value = os.path.expandvars(value) # expand environment variables
161 self.getLogger().debug("Add to %s: %s", name, value)
162 env = os.getenv(name, None)
164 os.environ[name] = value
166 os.environ[name] = value + separator + env
169 ###################################
170 # This begins the private section #
171 ###################################
173 def __parseArguments(self, args):
174 if len(args) == 0 or args[0].startswith("-"):
180 availableCommands = {
181 'start' : '_runAppli',
182 'shell' : '_runSession',
183 'connect' : '_runConsole',
184 'killall': '_killAll',
187 'coffee' : '_makeCoffee'
190 if not command in availableCommands.keys():
194 return availableCommands[command], options
199 Args consist in a mandatory command followed by optionnal parameters.
200 See usage for details on commands.
202 def _startSalome(self, args):
203 command, options = self.__parseArguments(args)
207 if args and args[0] in ["-h","--help","help"]:
210 # try to default to "start" command
211 command = "_runAppli"
214 res = getattr(self, command)(options) # run appropriate method
215 return res or (None, None)
216 except SystemExit, exc:
218 sys.exit(0) #catch sys.exit(0) happy end no warning
220 self.getLogger().warning("SystemExit 1 in method %s.", command)
222 except StandardError:
223 self.getLogger().error("Unexpected error:")
225 traceback.print_exc()
227 except SalomeContextException, e:
228 self.getLogger().error(e)
232 def __setEnvironmentFromConfigFile(self, filename, reserved=[]):
233 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
236 for var in unsetVars:
237 self.unsetVariable(var)
240 for reserved in reservedDict:
241 a = filter(None, reservedDict[reserved]) # remove empty elements
242 reformattedVals = ':'.join(a)
243 self.addToVariable(reserved, reformattedVals)
246 for key,val in configVars:
247 self.setVariable(key, val, overwrite=True)
250 sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
253 def _runAppli(self, args=[]):
254 # Initialize SALOME environment
255 sys.argv = ['runSalome'] + args
260 runSalome.runSalome()
263 def _runSession(self, args=[]):
264 sys.argv = ['runSession'] + args
266 runSession.configureSession(args)
271 scriptArgs = getScriptsAndArgs(args)
272 command = formatScriptsAndArgs(scriptArgs)
275 if sys.platform == "win32":
277 command = command.split(sep)
281 cmd = cmd.strip().split(' ')
282 #proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
283 proc = subprocess.Popen(cmd)
284 (stdoutdata, stderrdata) = proc.communicate()
285 if stdoutdata or stderrdata:
286 outmsg.append(stdoutdata)
287 errmsg.append(stderrdata)
289 return ("".join(outmsg), "".join(errmsg))
291 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
292 cmd = ["/bin/bash", "--rcfile", absoluteAppliPath + "/.bashrc" ]
293 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
297 def _runConsole(self, args=[]):
298 # Initialize SALOME environment
299 sys.argv = ['runConsole'] + args
303 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
304 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
308 def _killAll(self, args=[]):
309 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
311 import PortManager # mandatory
312 from multiprocessing import Process
313 from killSalomeWithPort import killMyPort
314 ports = PortManager.getBusyPorts()
319 with tempfile.NamedTemporaryFile():
320 p = Process(target = killMyPort, args=(port,))
324 p = Process(target = killMyPort, args=(2809,))
328 from killSalome import killAllPorts
334 def _showInfo(self, args=[]):
335 print "Running with python", platform.python_version()
336 self._runAppli(["--version"])
339 def _usage(self, unused=[]):
343 def _makeCoffee(self, args=[]):
346 print " ___...(-------)-....___"
347 print " .-\"\" ) ( \"\"-."
348 print " .-\'``\'|-._ ) _.-|"
349 print " / .--.| `\"\"---...........---\"\"` |"
357 print " _..---\"\"` \\ /`\"\"---.._"
358 print " .-\' \\ / \'-."
359 print " : `-.__ __.-\' :"
360 print " : ) \"\"---...---\"\" ( :"
361 print " \'._ `\"--...___...--\"` _.\'"
362 print " \\\"\"--..__ __..--\"\"/"
363 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
364 print " `\"\"--..,,_____ _____,,..--\"\"`"
365 print " `\"\"\"----\"\"\"`"
369 # Add the following two methods since logger is not pickable
370 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
371 def __getstate__(self):
372 d = dict(self.__dict__)
373 if hasattr(self, '_logger'):
377 def __setstate__(self, d):
378 self.__dict__.update(d) # I *think* this is a safe way to do it
380 # Excluding self._logger from pickle operation imply using the following method to access logger
382 if not hasattr(self, '_logger'):
383 self._logger = logging.getLogger(__name__)
384 #self._logger.setLevel(logging.DEBUG)
385 self._logger.setLevel(logging.ERROR)
391 if __name__ == "__main__":
392 if len(sys.argv) == 3:
393 context = pickle.loads(sys.argv[1])
394 args = pickle.loads(sys.argv[2])
395 (out, err) = context._startSalome(args)
397 sys.stdout.write(out)
399 sys.stderr.write(err)