1 # Copyright (C) 2013-2015 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
36 #exeName = os.path.splitext(os.path.basename(__file__))[0]
39 Usage: salome [command] [options] [--config=<file,folder,...>]
43 start Starts a SALOME session (through virtual application)
44 context Initializes SALOME context.
45 shell Initializes SALOME context, and executes scripts passed
47 connect Connects a Python console to the active SALOME session
48 killall Kill all SALOME running sessions for current user
49 info Display some information about SALOME
50 help Show this message
51 coffee Yes! SALOME can also make coffee!!
53 If no command is given, default to start.
57 Use salome <command> --help to show help on command ; available for start
60 --config=<file,folder,...>
61 ==========================
62 Initialize SALOME context from a list of context files and/or a list
63 of folders containing context files. The list is comma-separated, whithout
71 The SalomeContext class in an API to configure SALOME context then
72 start SALOME using a single python command.
77 Initialize context from a list of configuration files
78 identified by their names.
79 These files should be in appropriate (new .cfg) format.
80 However you can give old .sh environment files; in this case,
81 the SalomeContext class will try to automatically convert them
82 to .cfg format before setting the context.
84 def __init__(self, configFileNames=0):
85 #it could be None explicitely (if user use multiples setVariable...for standalone)
86 if configFileNames is None:
88 configFileNames = configFileNames or []
89 if len(configFileNames) == 0:
90 raise SalomeContextException("No configuration files given")
92 reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_PATH']
93 for filename in configFileNames:
94 basename, extension = os.path.splitext(filename)
95 if extension == ".cfg":
96 self.__setContextFromConfigFile(filename, reserved)
97 elif extension == ".sh":
98 #new convert procedures, temporary could be use not to be automatically deleted
99 #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
100 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
102 convertEnvFileToConfigFile(filename, temp.name, reserved)
103 self.__setContextFromConfigFile(temp.name, reserved)
105 except (ConfigParser.ParsingError, ValueError) as e:
106 self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
110 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
113 def __loadMPI(self, module_name):
114 print "Trying to load MPI module: %s..."%module_name,
116 out, err = subprocess.Popen(["modulecmd", "python", "load", module_name], stdout=subprocess.PIPE).communicate()
117 exec out # define specific environment variables
120 print " ** Failed **"
124 def runSalome(self, args):
126 # Run this module as a script, in order to use appropriate Python interpreter
127 # according to current path (initialized from context files).
128 mpi_module_option = "--with-mpi-module="
129 mpi_module = [x for x in args if x.startswith(mpi_module_option)]
131 mpi_module = mpi_module[0][len(mpi_module_option):]
132 self.__loadMPI(mpi_module)
133 args = [x for x in args if not x.startswith(mpi_module_option)]
135 mpi_module = os.getenv("SALOME_MPI_MODULE_NAME", None)
137 self.__loadMPI(mpi_module)
139 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
140 env_copy = os.environ.copy()
141 proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeContext.py"), pickle.dumps(self), pickle.dumps(args)], shell=False, close_fds=True, env=env_copy)
142 msg = proc.communicate()
143 return msg, proc.returncode
146 """Append value to PATH environment variable"""
147 def addToPath(self, value):
148 self.addToVariable('PATH', value)
151 """Append value to LD_LIBRARY_PATH environment variable"""
152 def addToLdLibraryPath(self, value):
153 self.addToVariable('LD_LIBRARY_PATH', value)
156 """Append value to DYLD_LIBRARY_PATH environment variable"""
157 def addToDyldLibraryPath(self, value):
158 self.addToVariable('DYLD_LIBRARY_PATH', value)
161 """Append value to PYTHONPATH environment variable"""
162 def addToPythonPath(self, value):
163 self.addToVariable('PYTHONPATH', value)
166 """Set environment variable to value"""
167 def setVariable(self, name, value, overwrite=False):
168 env = os.getenv(name, '')
169 if env and not overwrite:
170 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
174 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
176 value = os.path.expandvars(value) # expand environment variables
177 self.getLogger().debug("Set environment variable: %s=%s", name, value)
178 os.environ[name] = value
181 """Unset environment variable"""
182 def unsetVariable(self, name):
183 if os.environ.has_key(name):
187 """Append value to environment variable"""
188 def addToVariable(self, name, value, separator=os.pathsep):
192 value = os.path.expandvars(value) # expand environment variables
193 self.getLogger().debug("Add to %s: %s", name, value)
194 env = os.getenv(name, None)
196 os.environ[name] = value
198 os.environ[name] = value + separator + env
201 ###################################
202 # This begins the private section #
203 ###################################
205 def __parseArguments(self, args):
206 if len(args) == 0 or args[0].startswith("-"):
212 availableCommands = {
213 'start' : '_runAppli',
214 'context' : '_setContext',
215 'shell' : '_runSession',
216 'connect' : '_runConsole',
217 'killall': '_killAll',
220 'coffee' : '_makeCoffee'
223 if not command in availableCommands.keys():
227 return availableCommands[command], options
232 Args consist in a mandatory command followed by optionnal parameters.
233 See usage for details on commands.
235 def _startSalome(self, args):
238 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
240 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
241 if not path in sys.path:
242 sys.path[:0] = [path]
246 command, options = self.__parseArguments(args)
250 if args and args[0] in ["-h","--help","help"]:
253 # try to default to "start" command
254 command = "_runAppli"
257 res = getattr(self, command)(options) # run appropriate method
258 return res or (None, None)
259 except SystemExit, returncode:
261 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
263 except StandardError:
264 self.getLogger().error("Unexpected error:")
266 traceback.print_exc()
268 except SalomeContextException, e:
269 self.getLogger().error(e)
273 def __setContextFromConfigFile(self, filename, reserved=None):
277 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
278 except SalomeContextException, e:
280 file_dir = os.path.dirname(filename)
281 file_base = os.path.basename(filename)
282 base_no_ext, ext = os.path.splitext(file_base)
283 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
284 if ext == ".cfg" and os.path.isfile(sh_file):
285 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
286 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
288 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
289 self.__setContextFromConfigFile(temp.name, reserved)
291 self.getLogger().warning(msg)
294 except (ConfigParser.ParsingError, ValueError) as e:
295 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
296 self.getLogger().error(msg)
300 self.getLogger().error(msg)
304 for var in unsetVars:
305 self.unsetVariable(var)
308 for reserved in reservedDict:
309 a = filter(None, reservedDict[reserved]) # remove empty elements
310 a = [ os.path.realpath(x) for x in a ]
311 reformattedVals = os.pathsep.join(a)
312 if reserved in ["INCLUDE", "LIBPATH"]:
313 self.addToVariable(reserved, reformattedVals, separator=' ')
315 self.addToVariable(reserved, reformattedVals)
318 for key,val in configVars:
319 self.setVariable(key, val, overwrite=True)
322 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
323 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
324 sys.path[:0] = pythonpath
327 def _runAppli(self, args=None):
330 # Initialize SALOME context
331 sys.argv = ['runSalome'] + args
336 runSalome.runSalome()
339 def _setContext(self, args=None):
340 salome_context_set = os.getenv("SALOME_CONTEXT_SET")
341 if salome_context_set:
343 print "*** SALOME context has already been set."
344 print "*** Enter 'exit' (only once!) to leave SALOME context."
348 os.environ["SALOME_CONTEXT_SET"] = "yes"
350 print "*** SALOME context is now set."
351 print "*** Enter 'exit' (only once!) to leave SALOME context."
355 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
356 return proc.communicate()
359 def _runSession(self, args=None):
362 sys.argv = ['runSession'] + args
364 params, args = runSession.configureSession(args, exe="salome shell")
366 sys.argv = ['runSession'] + args
370 return runSession.runSession(params, args)
373 def _runConsole(self, args=None):
376 # Initialize SALOME context
377 sys.argv = ['runConsole'] + args
381 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
382 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
383 return proc.communicate()
386 def _killAll(self, args=None):
390 import PortManager # mandatory
391 from multiprocessing import Process
392 from killSalomeWithPort import killMyPort
393 ports = PortManager.getBusyPorts()
398 with tempfile.NamedTemporaryFile():
399 p = Process(target = killMyPort, args=(port,))
403 from killSalome import killAllPorts
409 def _showInfo(self, args=None):
410 print "Running with python", platform.python_version()
411 self._runAppli(["--version"])
414 def _usage(self, unused=None):
418 def _makeCoffee(self, args=None):
421 print " ___...(-------)-....___"
422 print " .-\"\" ) ( \"\"-."
423 print " .-\'``\'|-._ ) _.-|"
424 print " / .--.| `\"\"---...........---\"\"` |"
432 print " _..---\"\"` \\ /`\"\"---.._"
433 print " .-\' \\ / \'-."
434 print " : `-.__ __.-\' :"
435 print " : ) \"\"---...---\"\" ( :"
436 print " \'._ `\"--...___...--\"` _.\'"
437 print " \\\"\"--..__ __..--\"\"/"
438 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
439 print " `\"\"--..,,_____ _____,,..--\"\"`"
440 print " `\"\"\"----\"\"\"`"
444 # Add the following two methods since logger is not pickable
445 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
446 def __getstate__(self):
447 d = dict(self.__dict__)
448 if hasattr(self, '_logger'):
452 def __setstate__(self, d):
453 self.__dict__.update(d) # I *think* this is a safe way to do it
455 # Excluding self._logger from pickle operation imply using the following method to access logger
457 if not hasattr(self, '_logger'):
458 self._logger = logging.getLogger(__name__)
459 #self._logger.setLevel(logging.DEBUG)
460 #self._logger.setLevel(logging.WARNING)
461 self._logger.setLevel(logging.ERROR)
465 if __name__ == "__main__":
466 if len(sys.argv) == 3:
467 context = pickle.loads(sys.argv[1])
468 args = pickle.loads(sys.argv[2])
470 (out, err) = context._startSalome(args)
472 sys.stdout.write(out)
474 sys.stderr.write(err)