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
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 if platform.system() == 'Windows':
157 self.addToVariable('PATH', value)
159 self.addToVariable('LD_LIBRARY_PATH', value)
162 """Append value to DYLD_LIBRARY_PATH environment variable"""
163 def addToDyldLibraryPath(self, value):
164 self.addToVariable('DYLD_LIBRARY_PATH', value)
167 """Append value to PYTHONPATH environment variable"""
168 def addToPythonPath(self, value):
169 self.addToVariable('PYTHONPATH', value)
172 """Set environment variable to value"""
173 def setVariable(self, name, value, overwrite=False):
174 env = os.getenv(name, '')
175 if env and not overwrite:
176 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
180 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
182 value = os.path.expandvars(value) # expand environment variables
183 self.getLogger().debug("Set environment variable: %s=%s", name, value)
184 os.environ[name] = value
187 """Unset environment variable"""
188 def unsetVariable(self, name):
189 if os.environ.has_key(name):
193 """Append value to environment variable"""
194 def addToVariable(self, name, value, separator=os.pathsep):
198 value = os.path.expandvars(value) # expand environment variables
199 self.getLogger().debug("Add to %s: %s", name, value)
200 env = os.getenv(name, None)
202 os.environ[name] = value
204 os.environ[name] = value + separator + env
207 ###################################
208 # This begins the private section #
209 ###################################
211 def __parseArguments(self, args):
212 if len(args) == 0 or args[0].startswith("-"):
218 availableCommands = {
219 'start' : '_runAppli',
220 'context' : '_setContext',
221 'shell' : '_runSession',
222 'connect' : '_runConsole',
224 'killall' : '_killAll',
225 'test' : '_runTests',
226 'info' : '_showInfo',
228 'coffee' : '_makeCoffee',
232 if not command in availableCommands.keys():
236 return availableCommands[command], options
241 Args consist in a mandatory command followed by optionnal parameters.
242 See usage for details on commands.
244 def _startSalome(self, args):
248 from setenv import add_path
249 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
250 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
251 add_path(path, "PYTHONPATH")
252 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
253 add_path(path, "PYTHONPATH")
258 command, options = self.__parseArguments(args)
262 if args and args[0] in ["-h","--help","help"]:
265 # try to default to "start" command
266 command = "_runAppli"
269 res = getattr(self, command)(options) # run appropriate method
270 return res or (None, None)
271 except SystemExit, returncode:
273 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
275 except StandardError:
276 self.getLogger().error("Unexpected error:")
278 traceback.print_exc()
280 except SalomeContextException, e:
281 self.getLogger().error(e)
285 def __setContextFromConfigFile(self, filename, reserved=None):
289 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
290 except SalomeContextException, e:
292 file_dir = os.path.dirname(filename)
293 file_base = os.path.basename(filename)
294 base_no_ext, ext = os.path.splitext(file_base)
295 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
296 if ext == ".cfg" and os.path.isfile(sh_file):
297 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
298 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
300 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
301 self.__setContextFromConfigFile(temp.name, reserved)
303 self.getLogger().warning(msg)
306 except (ConfigParser.ParsingError, ValueError) as e:
307 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
308 self.getLogger().error(msg)
312 self.getLogger().error(msg)
316 for var in unsetVars:
317 self.unsetVariable(var)
320 for reserved in reservedDict:
321 a = filter(None, reservedDict[reserved]) # remove empty elements
322 a = [ os.path.realpath(x) for x in a ]
323 reformattedVals = os.pathsep.join(a)
324 if reserved in ["INCLUDE", "LIBPATH"]:
325 self.addToVariable(reserved, reformattedVals, separator=' ')
327 self.addToVariable(reserved, reformattedVals)
330 for key,val in configVars:
331 self.setVariable(key, val, overwrite=True)
334 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
335 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
336 sys.path[:0] = pythonpath
339 def _runAppli(self, args=None):
342 # Initialize SALOME environment
343 sys.argv = ['runSalome'] + args
345 setenv.main(True, exeName="salome start")
348 runSalome.runSalome()
351 def _setContext(self, args=None):
352 salome_context_set = os.getenv("SALOME_CONTEXT_SET")
353 if salome_context_set:
355 print "*** SALOME context has already been set."
356 print "*** Enter 'exit' (only once!) to leave SALOME context."
360 os.environ["SALOME_CONTEXT_SET"] = "yes"
362 print "*** SALOME context is now set."
363 print "*** Enter 'exit' (only once!) to leave SALOME context."
367 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
368 return proc.communicate()
371 def _runSession(self, args=None):
374 sys.argv = ['runSession'] + args
376 params, args = runSession.configureSession(args, exe="salome shell")
378 sys.argv = ['runSession'] + args
382 return runSession.runSession(params, args)
385 def _runConsole(self, args=None):
388 # Initialize SALOME environment
389 sys.argv = ['runConsole']
394 return runConsole.connect(args)
397 def _kill(self, args=None):
402 print "Port number(s) not provided to command: salome kill <port(s)>"
405 from multiprocessing import Process
406 from killSalomeWithPort import killMyPort
409 with tempfile.NamedTemporaryFile():
410 p = Process(target = killMyPort, args=(port,))
416 def _killAll(self, unused=None):
418 import PortManager # mandatory
419 from multiprocessing import Process
420 from killSalomeWithPort import killMyPort
421 ports = PortManager.getBusyPorts()
426 with tempfile.NamedTemporaryFile():
427 p = Process(target = killMyPort, args=(port,))
431 from killSalome import killAllPorts
436 def _runTests(self, args=None):
439 sys.argv = ['runTests']
444 return runTests.runTests(args, exe="salome test")
447 def _showInfo(self, args=None):
451 usage = "Usage: salome info [options]"
453 Display some information about SALOME.\n
454 Available options are:
455 -p,--ports Show list of busy ports (running SALOME instances).
456 -v,--version Show running SALOME version.
457 -h,--help Show this message.
462 if "-h" in args or "--help" in args:
466 if "-p" in args or "--ports" in args:
468 ports = PortManager.getBusyPorts()
469 print "SALOME instances are running on ports:", ports
471 print "Last started instance on port %s"%ports[-1]
473 if "-v" in args or "--version" in args:
474 print "Running with python", platform.python_version()
475 self._runAppli(["--version"])
478 def _usage(self, unused=None):
482 def _makeCoffee(self, unused=None):
485 print " ___...(-------)-....___"
486 print " .-\"\" ) ( \"\"-."
487 print " .-\'``\'|-._ ) _.-|"
488 print " / .--.| `\"\"---...........---\"\"` |"
493 print " `\\ `| SALOME |"
494 print " _/ /\\ 4 EVER /"
495 print " (__/ \\ <3 /"
496 print " _..---\"\"` \\ /`\"\"---.._"
497 print " .-\' \\ / \'-."
498 print " : `-.__ __.-\' :"
499 print " : ) \"\"---...---\"\" ( :"
500 print " \'._ `\"--...___...--\"` _.\'"
501 print " \\\"\"--..__ __..--\"\"/"
502 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
503 print " `\"\"--..,,_____ _____,,..--\"\"`"
504 print " `\"\"\"----\"\"\"`"
506 print " SALOME is working for you; what else?"
511 def _getCar(self, unused=None):
512 print " _____________"
513 print " ..---:::::::-----------. ::::;;."
514 print " .\'\"\"\"\"\"\" ;; \\ \":."
515 print " .\'\' ; \\ \"\\__."
516 print " .\' ;; ; \\\\\";"
517 print " .\' ; _____; \\\\/"
518 print " .\' :; ;\" \\ ___:\'."
519 print " .\'--........................... : = ____:\" \\ \\"
520 print " ..-\"\" \"\"\"\' o\"\"\" ; ; :"
521 print " .--\"\" .----- ..----... _.- --. ..-\" ; ; ; ;"
522 print " .\"\"_- \"--\"\"-----\'\"\" _-\" .-\"\" ; ; .-."
523 print " .\' .\' SALOME .\" .\" ; ; /. |"
524 print " /-./\' 4 EVER <3 .\" / _.. ; ; ;;;|"
525 print " : ;-.______ / _________==. /_ \\ ; ; ;;;;"
526 print " ; / | \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\" : /\" \". |; ; _; ;;;"
527 print " /\"-/ | / / / / ;|; ;-\" | ;\';"
528 print ":- : \"\"\"----______ / / ____. . .\"\'. ;; .-\"..T\" ."
529 print "\'. \" ___ \"\": \'\"\"\"\"\"\"\"\"\"\"\"\"\"\" . ; ; ;; ;.\" .\" \'--\""
530 print " \", __ \"\"\" \"\"---... :- - - - - - - - - \' \' ; ; ; ;;\" .\""
531 print " /. ; \"\"\"---___ ; ; ; ;|.\"\""
532 print " : \": \"\"\"----. .-------. ; ; ; ;:"
533 print " \\ \'--__ \\ \\ \\ / | ; ;;"
534 print " \'-.. \"\"\"\"---___ : .______..\\ __/..-\"\"| ; ; ;"
535 print " \"\"--.. \"\"\"--\" m l s . \". . ;"
536 print " \"\"------... ..--\"\" \" :"
537 print " \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" \\ /"
540 print " Drive your simulation properly with SALOME!"
545 # Add the following two methods since logger is not pickable
546 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
547 def __getstate__(self):
548 d = dict(self.__dict__)
549 if hasattr(self, '_logger'):
553 def __setstate__(self, d):
554 self.__dict__.update(d) # I *think* this is a safe way to do it
556 # Excluding self._logger from pickle operation imply using the following method to access logger
558 if not hasattr(self, '_logger'):
559 self._logger = logging.getLogger(__name__)
560 #self._logger.setLevel(logging.DEBUG)
561 #self._logger.setLevel(logging.WARNING)
562 self._logger.setLevel(logging.ERROR)
566 if __name__ == "__main__":
567 if len(sys.argv) == 3:
568 context = pickle.loads(sys.argv[1])
569 args = pickle.loads(sys.argv[2])
571 (out, err) = context._startSalome(args)
573 sys.stdout.write(out)
575 sys.stderr.write(err)