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=0):
73 #it could be None explicitely (if user use multiples setVariable...for standalone)
74 if configFileNames is None:
76 configFileNames = configFileNames or []
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()
114 # self._killAll(args)
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=None):
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=None):
285 # Initialize SALOME environment
286 sys.argv = ['runSalome'] + args
291 runSalome.runSalome()
294 def _runSession(self, args=None):
297 sys.argv = ['runSession'] + args
299 runSession.configureSession(args)
304 scriptArgs = getScriptsAndArgs(args)
305 command = formatScriptsAndArgs(scriptArgs)
306 return runSession.runSession(command)
309 def _runConsole(self, args=None):
312 # Initialize SALOME environment
313 sys.argv = ['runConsole'] + args
317 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
318 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
319 return proc.communicate()
322 def _killAll(self, args=None):
326 import PortManager # mandatory
327 from multiprocessing import Process
328 from killSalomeWithPort import killMyPort
329 ports = PortManager.getBusyPorts()
334 with tempfile.NamedTemporaryFile():
335 p = Process(target = killMyPort, args=(port,))
339 from killSalome import killAllPorts
345 def _showInfo(self, args=None):
346 print "Running with python", platform.python_version()
347 self._runAppli(["--version"])
350 def _usage(self, unused=None):
354 def _makeCoffee(self, args=None):
357 print " ___...(-------)-....___"
358 print " .-\"\" ) ( \"\"-."
359 print " .-\'``\'|-._ ) _.-|"
360 print " / .--.| `\"\"---...........---\"\"` |"
368 print " _..---\"\"` \\ /`\"\"---.._"
369 print " .-\' \\ / \'-."
370 print " : `-.__ __.-\' :"
371 print " : ) \"\"---...---\"\" ( :"
372 print " \'._ `\"--...___...--\"` _.\'"
373 print " \\\"\"--..__ __..--\"\"/"
374 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
375 print " `\"\"--..,,_____ _____,,..--\"\"`"
376 print " `\"\"\"----\"\"\"`"
380 # Add the following two methods since logger is not pickable
381 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
382 def __getstate__(self):
383 d = dict(self.__dict__)
384 if hasattr(self, '_logger'):
388 def __setstate__(self, d):
389 self.__dict__.update(d) # I *think* this is a safe way to do it
391 # Excluding self._logger from pickle operation imply using the following method to access logger
393 if not hasattr(self, '_logger'):
394 self._logger = logging.getLogger(__name__)
395 #self._logger.setLevel(logging.DEBUG)
396 #self._logger.setLevel(logging.WARNING)
397 self._logger.setLevel(logging.ERROR)
401 if __name__ == "__main__":
402 if len(sys.argv) == 3:
403 context = pickle.loads(sys.argv[1])
404 args = pickle.loads(sys.argv[2])
406 (out, err) = context._startSalome(args)
408 sys.stdout.write(out)
410 sys.stderr.write(err)