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)
114 msg = proc.communicate()
120 """Append value to PATH environment variable"""
121 def addToPath(self, value):
122 self.addToVariable('PATH', value)
125 """Append value to LD_LIBRARY_PATH environment variable"""
126 def addToLdLibraryPath(self, value):
127 self.addToVariable('LD_LIBRARY_PATH', value)
130 """Append value to PYTHONPATH environment variable"""
131 def addToPythonPath(self, value):
132 self.addToVariable('PYTHONPATH', value)
135 """Set environment variable to value"""
136 def setVariable(self, name, value, overwrite=False):
137 env = os.getenv(name, '')
138 if env and not overwrite:
139 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
143 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
145 value = os.path.expandvars(value) # expand environment variables
146 self.getLogger().debug("Set environment variable: %s=%s", name, value)
147 os.environ[name] = value
150 """Unset environment variable"""
151 def unsetVariable(self, name):
152 if os.environ.has_key(name):
156 """Append value to environment variable"""
157 def addToVariable(self, name, value, separator=os.pathsep):
161 value = os.path.expandvars(value) # expand environment variables
162 self.getLogger().debug("Add to %s: %s", name, value)
163 env = os.getenv(name, None)
165 os.environ[name] = value
167 os.environ[name] = value + separator + env
170 ###################################
171 # This begins the private section #
172 ###################################
174 def __parseArguments(self, args):
175 if len(args) == 0 or args[0].startswith("-"):
181 availableCommands = {
182 'start' : '_runAppli',
183 'shell' : '_runSession',
184 'connect' : '_runConsole',
185 'killall': '_killAll',
188 'coffee' : '_makeCoffee'
191 if not command in availableCommands.keys():
195 return availableCommands[command], options
200 Args consist in a mandatory command followed by optionnal parameters.
201 See usage for details on commands.
203 def _startSalome(self, args):
204 command, options = self.__parseArguments(args)
208 if args and args[0] in ["-h","--help","help"]:
211 # try to default to "start" command
212 command = "_runAppli"
215 res = getattr(self, command)(options) # run appropriate method
216 return res or (None, None)
217 except SystemExit, exc:
219 sys.exit(0) #catch sys.exit(0) happy end no warning
221 self.getLogger().warning("SystemExit 1 in method %s.", command)
223 except StandardError:
224 self.getLogger().error("Unexpected error:")
226 traceback.print_exc()
228 except SalomeContextException, e:
229 self.getLogger().error(e)
233 def __setEnvironmentFromConfigFile(self, filename, reserved=[]):
234 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
237 for var in unsetVars:
238 self.unsetVariable(var)
241 for reserved in reservedDict:
242 a = filter(None, reservedDict[reserved]) # remove empty elements
243 reformattedVals = ':'.join(a)
244 self.addToVariable(reserved, reformattedVals)
247 for key,val in configVars:
248 self.setVariable(key, val, overwrite=True)
251 sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
254 def _runAppli(self, args=[]):
255 # Initialize SALOME environment
256 sys.argv = ['runSalome'] + args
261 runSalome.runSalome()
264 def _runSession(self, args=[]):
265 sys.argv = ['runSession'] + args
267 runSession.configureSession(args)
272 scriptArgs = getScriptsAndArgs(args)
273 command = formatScriptsAndArgs(scriptArgs)
276 if sys.platform == "win32":
278 command = command.split(sep)
282 cmd = cmd.strip().split(' ')
283 #proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
284 proc = subprocess.Popen(cmd)
285 (stdoutdata, stderrdata) = proc.communicate()
286 if stdoutdata or stderrdata:
287 outmsg.append(stdoutdata)
288 errmsg.append(stderrdata)
290 return ("".join(outmsg), "".join(errmsg))
292 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
293 cmd = ["/bin/bash", "--rcfile", absoluteAppliPath + "/.bashrc" ]
294 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
295 return proc.communicate()
298 def _runConsole(self, args=[]):
299 # Initialize SALOME environment
300 sys.argv = ['runConsole'] + args
304 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
305 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
306 return proc.communicate()
309 def _killAll(self, args=[]):
310 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
312 import PortManager # mandatory
313 from multiprocessing import Process
314 from killSalomeWithPort import killMyPort
315 ports = PortManager.getBusyPorts()
320 with tempfile.NamedTemporaryFile():
321 p = Process(target = killMyPort, args=(port,))
325 p = Process(target = killMyPort, args=(2809,))
329 from killSalome import killAllPorts
335 def _showInfo(self, args=[]):
336 print "Running with python", platform.python_version()
337 self._runAppli(["--version"])
340 def _usage(self, unused=[]):
344 def _makeCoffee(self, args=[]):
347 print " ___...(-------)-....___"
348 print " .-\"\" ) ( \"\"-."
349 print " .-\'``\'|-._ ) _.-|"
350 print " / .--.| `\"\"---...........---\"\"` |"
358 print " _..---\"\"` \\ /`\"\"---.._"
359 print " .-\' \\ / \'-."
360 print " : `-.__ __.-\' :"
361 print " : ) \"\"---...---\"\" ( :"
362 print " \'._ `\"--...___...--\"` _.\'"
363 print " \\\"\"--..__ __..--\"\"/"
364 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
365 print " `\"\"--..,,_____ _____,,..--\"\"`"
366 print " `\"\"\"----\"\"\"`"
370 # Add the following two methods since logger is not pickable
371 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
372 def __getstate__(self):
373 d = dict(self.__dict__)
374 if hasattr(self, '_logger'):
378 def __setstate__(self, d):
379 self.__dict__.update(d) # I *think* this is a safe way to do it
381 # Excluding self._logger from pickle operation imply using the following method to access logger
383 if not hasattr(self, '_logger'):
384 self._logger = logging.getLogger(__name__)
385 #self._logger.setLevel(logging.DEBUG)
386 self._logger.setLevel(logging.ERROR)
392 if __name__ == "__main__":
393 if len(sys.argv) == 3:
394 context = pickle.loads(sys.argv[1])
395 args = pickle.loads(sys.argv[2])
396 (out, err) = context._startSalome(args)
398 sys.stdout.write(out)
400 sys.stderr.write(err)