1 # Copyright (C) 2013-2016 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
37 Usage: salome [command] [options] [--config=<file,folder,...>]
41 start Start a new SALOME instance.
42 context Initialize SALOME context. Current environment is extended.
43 shell Initialize SALOME context, attached to the last created SALOME
44 instance if any, and executes scripts passed as command arguments.
45 User works in a Shell terminal; SALOME environment is set but
46 application is not started.
47 connect Connect a Python console to the active SALOME instance.
48 kill <port(s)> Terminate SALOME instances running on given ports for current user.
49 Port numbers must be separated by blank characters.
50 killall Terminate *all* SALOME running instances for current user.
51 Do not start a new one.
52 test Run SALOME tests.
53 info Display some information about SALOME.
54 help Show this message.
56 If no command is given, default to start.
60 Use salome <command> --help to show help on command ; available for commands:
61 start, shell, connect, test, info.
63 --config=<file,folder,...>
64 ==========================
65 Initialize SALOME context from a list of context files and/or a list
66 of folders containing context files. The list is comma-separated, whithout
74 The SalomeContext class in an API to configure SALOME context then
75 start SALOME using a single python command.
80 Initialize context from a list of configuration files
81 identified by their names.
82 These files should be in appropriate (new .cfg) format.
83 However you can give old .sh environment files; in this case,
84 the SalomeContext class will try to automatically convert them
85 to .cfg format before setting the context.
87 def __init__(self, configFileNames=0):
88 #it could be None explicitely (if user use multiples setVariable...for standalone)
89 if configFileNames is None:
91 configFileNames = configFileNames or []
92 if len(configFileNames) == 0:
93 raise SalomeContextException("No configuration files given")
95 reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_PATH']
96 for filename in configFileNames:
97 basename, extension = os.path.splitext(filename)
98 if extension == ".cfg":
99 self.__setContextFromConfigFile(filename, reserved)
100 elif extension == ".sh":
101 #new convert procedures, temporary could be use not to be automatically deleted
102 #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
103 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
105 convertEnvFileToConfigFile(filename, temp.name, reserved)
106 self.__setContextFromConfigFile(temp.name, reserved)
108 except (ConfigParser.ParsingError, ValueError) as e:
109 self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
113 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
116 def __loadMPI(self, module_name):
117 print "Trying to load MPI module: %s..."%module_name,
119 out, err = subprocess.Popen(["modulecmd", "python", "load", module_name], stdout=subprocess.PIPE).communicate()
120 exec out # define specific environment variables
123 print " ** Failed **"
127 def runSalome(self, args):
129 # Run this module as a script, in order to use appropriate Python interpreter
130 # according to current path (initialized from context files).
131 mpi_module_option = "--with-mpi-module="
132 mpi_module = [x for x in args if x.startswith(mpi_module_option)]
134 mpi_module = mpi_module[0][len(mpi_module_option):]
135 self.__loadMPI(mpi_module)
136 args = [x for x in args if not x.startswith(mpi_module_option)]
138 mpi_module = os.getenv("SALOME_MPI_MODULE_NAME", None)
140 self.__loadMPI(mpi_module)
142 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
143 env_copy = os.environ.copy()
144 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)
145 msg = proc.communicate()
146 return msg, proc.returncode
149 """Append value to PATH environment variable"""
150 def addToPath(self, value):
151 self.addToVariable('PATH', value)
154 """Append value to LD_LIBRARY_PATH environment variable"""
155 def addToLdLibraryPath(self, value):
156 self.addToVariable('LD_LIBRARY_PATH', value)
159 """Append value to DYLD_LIBRARY_PATH environment variable"""
160 def addToDyldLibraryPath(self, value):
161 self.addToVariable('DYLD_LIBRARY_PATH', value)
164 """Append value to PYTHONPATH environment variable"""
165 def addToPythonPath(self, value):
166 self.addToVariable('PYTHONPATH', value)
169 """Set environment variable to value"""
170 def setVariable(self, name, value, overwrite=False):
171 env = os.getenv(name, '')
172 if env and not overwrite:
173 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
177 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
179 value = os.path.expandvars(value) # expand environment variables
180 self.getLogger().debug("Set environment variable: %s=%s", name, value)
181 os.environ[name] = value
184 """Unset environment variable"""
185 def unsetVariable(self, name):
186 if os.environ.has_key(name):
190 """Append value to environment variable"""
191 def addToVariable(self, name, value, separator=os.pathsep):
195 value = os.path.expandvars(value) # expand environment variables
196 self.getLogger().debug("Add to %s: %s", name, value)
197 env = os.getenv(name, None)
199 os.environ[name] = value
201 os.environ[name] = value + separator + env
204 ###################################
205 # This begins the private section #
206 ###################################
208 def __parseArguments(self, args):
209 if len(args) == 0 or args[0].startswith("-"):
215 availableCommands = {
216 'start' : '_runAppli',
217 'context' : '_setContext',
218 'shell' : '_runSession',
219 'connect' : '_runConsole',
221 'killall' : '_killAll',
222 'test' : '_runTests',
223 'info' : '_showInfo',
225 'coffee' : '_makeCoffee',
229 if not command in availableCommands.keys():
233 return availableCommands[command], options
238 Args consist in a mandatory command followed by optionnal parameters.
239 See usage for details on commands.
241 def _startSalome(self, args):
245 from setenv import add_path
246 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
247 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
248 add_path(path, "PYTHONPATH")
249 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
250 add_path(path, "PYTHONPATH")
255 command, options = self.__parseArguments(args)
259 if args and args[0] in ["-h","--help","help"]:
262 # try to default to "start" command
263 command = "_runAppli"
266 res = getattr(self, command)(options) # run appropriate method
267 return res or (None, None)
268 except SystemExit, returncode:
270 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
272 except StandardError:
273 self.getLogger().error("Unexpected error:")
275 traceback.print_exc()
277 except SalomeContextException, e:
278 self.getLogger().error(e)
282 def __setContextFromConfigFile(self, filename, reserved=None):
286 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
287 except SalomeContextException, e:
289 file_dir = os.path.dirname(filename)
290 file_base = os.path.basename(filename)
291 base_no_ext, ext = os.path.splitext(file_base)
292 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
293 if ext == ".cfg" and os.path.isfile(sh_file):
294 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
295 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
297 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
298 self.__setContextFromConfigFile(temp.name, reserved)
300 self.getLogger().warning(msg)
303 except (ConfigParser.ParsingError, ValueError) as e:
304 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
305 self.getLogger().error(msg)
309 self.getLogger().error(msg)
313 for var in unsetVars:
314 self.unsetVariable(var)
317 for reserved in reservedDict:
318 a = filter(None, reservedDict[reserved]) # remove empty elements
319 a = [ os.path.realpath(x) for x in a ]
320 reformattedVals = os.pathsep.join(a)
321 if reserved in ["INCLUDE", "LIBPATH"]:
322 self.addToVariable(reserved, reformattedVals, separator=' ')
324 self.addToVariable(reserved, reformattedVals)
327 for key,val in configVars:
328 self.setVariable(key, val, overwrite=True)
331 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
332 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
333 sys.path[:0] = pythonpath
336 def _runAppli(self, args=None):
339 # Initialize SALOME environment
340 sys.argv = ['runSalome'] + args
342 setenv.main(True, exeName="salome start")
345 runSalome.runSalome()
348 def _setContext(self, args=None):
349 salome_context_set = os.getenv("SALOME_CONTEXT_SET")
350 if salome_context_set:
352 print "*** SALOME context has already been set."
353 print "*** Enter 'exit' (only once!) to leave SALOME context."
357 os.environ["SALOME_CONTEXT_SET"] = "yes"
359 print "*** SALOME context is now set."
360 print "*** Enter 'exit' (only once!) to leave SALOME context."
364 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
365 return proc.communicate()
368 def _runSession(self, args=None):
371 sys.argv = ['runSession'] + args
373 params, args = runSession.configureSession(args, exe="salome shell")
375 sys.argv = ['runSession'] + args
379 return runSession.runSession(params, args)
382 def _runConsole(self, args=None):
385 # Initialize SALOME environment
386 sys.argv = ['runConsole']
391 return runConsole.connect(args)
394 def _kill(self, args=None):
399 print "Port number(s) not provided to command: salome kill <port(s)>"
402 from multiprocessing import Process
403 from killSalomeWithPort import killMyPort
406 with tempfile.NamedTemporaryFile():
407 p = Process(target = killMyPort, args=(port,))
413 def _killAll(self, unused=None):
415 import PortManager # mandatory
416 from multiprocessing import Process
417 from killSalomeWithPort import killMyPort
418 ports = PortManager.getBusyPorts()
423 with tempfile.NamedTemporaryFile():
424 p = Process(target = killMyPort, args=(port,))
428 # :TODO: should be declared obsolete
429 from killSalome import killAllPorts
434 def _runTests(self, args=None):
437 sys.argv = ['runTests']
442 return runTests.runTests(args, exe="salome test")
445 def _showInfo(self, args=None):
449 usage = "Usage: salome info [options]"
451 Display some information about SALOME.\n
452 Available options are:
453 -p,--ports Show list of busy ports (running SALOME instances).
454 -v,--version Show running SALOME version.
455 -h,--help Show this message.
460 if "-h" in args or "--help" in args:
464 if "-p" in args or "--ports" in args:
466 ports = PortManager.getBusyPorts()
467 print "SALOME instances are running on ports:", ports
469 print "Last started instance on port %s"%ports[-1]
471 if "-v" in args or "--version" in args:
472 print "Running with python", platform.python_version()
473 self._runAppli(["--version"])
476 def _usage(self, unused=None):
480 def _makeCoffee(self, unused=None):
483 print " ___...(-------)-....___"
484 print " .-\"\" ) ( \"\"-."
485 print " .-\'``\'|-._ ) _.-|"
486 print " / .--.| `\"\"---...........---\"\"` |"
491 print " `\\ `| SALOME |"
492 print " _/ /\\ 4 EVER /"
493 print " (__/ \\ <3 /"
494 print " _..---\"\"` \\ /`\"\"---.._"
495 print " .-\' \\ / \'-."
496 print " : `-.__ __.-\' :"
497 print " : ) \"\"---...---\"\" ( :"
498 print " \'._ `\"--...___...--\"` _.\'"
499 print " \\\"\"--..__ __..--\"\"/"
500 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
501 print " `\"\"--..,,_____ _____,,..--\"\"`"
502 print " `\"\"\"----\"\"\"`"
504 print " SALOME is working for you; what else?"
509 def _getCar(self, unused=None):
510 print " _____________"
511 print " ..---:::::::-----------. ::::;;."
512 print " .\'\"\"\"\"\"\" ;; \\ \":."
513 print " .\'\' ; \\ \"\\__."
514 print " .\' ;; ; \\\\\";"
515 print " .\' ; _____; \\\\/"
516 print " .\' :; ;\" \\ ___:\'."
517 print " .\'--........................... : = ____:\" \\ \\"
518 print " ..-\"\" \"\"\"\' o\"\"\" ; ; :"
519 print " .--\"\" .----- ..----... _.- --. ..-\" ; ; ; ;"
520 print " .\"\"_- \"--\"\"-----\'\"\" _-\" .-\"\" ; ; .-."
521 print " .\' .\' SALOME .\" .\" ; ; /. |"
522 print " /-./\' 4 EVER <3 .\" / _.. ; ; ;;;|"
523 print " : ;-.______ / _________==. /_ \\ ; ; ;;;;"
524 print " ; / | \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\" : /\" \". |; ; _; ;;;"
525 print " /\"-/ | / / / / ;|; ;-\" | ;\';"
526 print ":- : \"\"\"----______ / / ____. . .\"\'. ;; .-\"..T\" ."
527 print "\'. \" ___ \"\": \'\"\"\"\"\"\"\"\"\"\"\"\"\"\" . ; ; ;; ;.\" .\" \'--\""
528 print " \", __ \"\"\" \"\"---... :- - - - - - - - - \' \' ; ; ; ;;\" .\""
529 print " /. ; \"\"\"---___ ; ; ; ;|.\"\""
530 print " : \": \"\"\"----. .-------. ; ; ; ;:"
531 print " \\ \'--__ \\ \\ \\ / | ; ;;"
532 print " \'-.. \"\"\"\"---___ : .______..\\ __/..-\"\"| ; ; ;"
533 print " \"\"--.. \"\"\"--\" m l s . \". . ;"
534 print " \"\"------... ..--\"\" \" :"
535 print " \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" \\ /"
538 print " Drive your simulation properly with SALOME!"
543 # Add the following two methods since logger is not pickable
544 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
545 def __getstate__(self):
546 d = dict(self.__dict__)
547 if hasattr(self, '_logger'):
551 def __setstate__(self, d):
552 self.__dict__.update(d) # I *think* this is a safe way to do it
554 # Excluding self._logger from pickle operation imply using the following method to access logger
556 if not hasattr(self, '_logger'):
557 self._logger = logging.getLogger(__name__)
558 #self._logger.setLevel(logging.DEBUG)
559 #self._logger.setLevel(logging.WARNING)
560 self._logger.setLevel(logging.ERROR)
564 if __name__ == "__main__":
565 if len(sys.argv) == 3:
566 context = pickle.loads(sys.argv[1])
567 args = pickle.loads(sys.argv[2])
569 (out, err) = context._startSalome(args)
571 sys.stdout.write(out)
573 sys.stderr.write(err)