1 # Copyright (C) 2013-2017 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
32 from salomeContextUtils import SalomeContextException
36 Usage: salome [command] [options] [--config=<file,folder,...>]
40 start Start a new SALOME instance.
41 context Initialize SALOME context. Current environment is extended.
42 shell Initialize SALOME context, attached to the last created SALOME
43 instance if any, and executes scripts passed as command arguments.
44 User works in a Shell terminal. SALOME environment is set but
45 application is not started.
46 connect Connect a Python console to the active SALOME instance.
47 kill <port(s)> Terminate SALOME instances running on given ports for current user.
48 Port numbers must be separated by blank characters.
49 killall Terminate *all* SALOME running instances for current user.
50 Do not start a new one.
51 test Run SALOME tests.
52 info Display some information about SALOME.
53 doc <module(s)> Show online module documentation (if available).
54 Module names must be separated by blank characters.
55 help Show this message.
57 If no command is given, default is start.
61 Use salome <command> --help to show help on command. Available for the
62 following commands: start, shell, connect, test, info.
64 --config=<file,folder,...>
65 ==========================
66 Initialize SALOME context from a list of context files and/or a list
67 of folders containing context files. The list is comma-separated, whithout
75 The SalomeContext class in an API to configure SALOME context then
76 start SALOME using a single python command.
81 Initialize context from a list of configuration files
82 identified by their names.
83 These files should be in appropriate .cfg format.
85 def __init__(self, configFileNames=0):
86 self.getLogger().setLevel(logging.INFO)
87 #it could be None explicitely (if user use multiples setVariable...for standalone)
88 if configFileNames is None:
90 configFileNames = configFileNames or []
91 if len(configFileNames) == 0:
92 raise SalomeContextException("No configuration files given")
94 reserved=['PATH', 'DYLD_FALLBACK_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_PATH', 'LIBRARY_PATH']
95 for filename in configFileNames:
96 basename, extension = os.path.splitext(filename)
97 if extension == ".cfg":
98 self.__setContextFromConfigFile(filename, reserved)
100 self.getLogger().error("Unrecognized extension for configuration file: %s", filename)
103 def __loadMPI(self, module_name):
104 print "Trying to load MPI module: %s..."%module_name,
106 out, err = subprocess.Popen(["modulecmd", "python", "load", module_name], stdout=subprocess.PIPE).communicate()
107 exec out # define specific environment variables
110 print " ** Failed **"
114 def runSalome(self, args):
116 # Run this module as a script, in order to use appropriate Python interpreter
117 # according to current path (initialized from context files).
118 mpi_module_option = "--with-mpi-module="
119 mpi_module = [x for x in args if x.startswith(mpi_module_option)]
121 mpi_module = mpi_module[0][len(mpi_module_option):]
122 self.__loadMPI(mpi_module)
123 args = [x for x in args if not x.startswith(mpi_module_option)]
125 mpi_module = os.getenv("SALOME_MPI_MODULE_NAME", None)
127 self.__loadMPI(mpi_module)
129 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
130 env_copy = os.environ.copy()
131 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)
132 out, err = proc.communicate()
133 return out, err, proc.returncode
136 """Append value to PATH environment variable"""
137 def addToPath(self, value):
138 self.addToVariable('PATH', value)
141 """Append value to LD_LIBRARY_PATH environment variable"""
142 def addToLdLibraryPath(self, value):
143 if platform.system() == 'Windows':
144 self.addToVariable('PATH', value)
145 elif platform.system() == 'Darwin':
146 if "LAPACK" in value:
147 self.addToVariable('DYLD_FALLBACK_LIBRARY_PATH', value)
149 self.addToVariable('DYLD_LIBRARY_PATH', value)
151 self.addToVariable('LD_LIBRARY_PATH', value)
154 """Append value to DYLD_LIBRARY_PATH environment variable"""
155 def addToDyldLibraryPath(self, value):
156 self.addToVariable('DYLD_LIBRARY_PATH', value)
159 """Append value to PYTHONPATH environment variable"""
160 def addToPythonPath(self, value):
161 self.addToVariable('PYTHONPATH', value)
164 """Set environment variable to value"""
165 def setVariable(self, name, value, overwrite=False):
166 env = os.getenv(name, '')
167 if env and not overwrite:
168 self.getLogger().error("Environment variable already existing (and not overwritten): %s=%s", name, value)
172 self.getLogger().debug("Overwriting environment variable: %s=%s", name, value)
174 value = os.path.expandvars(value) # expand environment variables
175 self.getLogger().debug("Set environment variable: %s=%s", name, value)
176 os.environ[name] = value
179 """Unset environment variable"""
180 def unsetVariable(self, name):
181 if os.environ.has_key(name):
185 """Append value to environment variable"""
186 def addToVariable(self, name, value, separator=os.pathsep):
190 value = os.path.expandvars(value) # expand environment variables
191 self.getLogger().debug("Add to %s: %s", name, value)
192 env = os.getenv(name, None)
194 os.environ[name] = value
196 os.environ[name] = value + separator + env
199 ###################################
200 # This begins the private section #
201 ###################################
203 def __parseArguments(self, args):
204 if len(args) == 0 or args[0].startswith("-"):
210 availableCommands = {
211 'start' : '_runAppli',
212 'context' : '_setContext',
213 'shell' : '_runSession',
214 'connect' : '_runConsole',
216 'killall' : '_killAll',
217 'test' : '_runTests',
218 'info' : '_showInfo',
221 'coffee' : '_makeCoffee',
225 if not command in availableCommands.keys():
229 return availableCommands[command], options
234 Args consist in a mandatory command followed by optionnal parameters.
235 See usage for details on commands.
237 def _startSalome(self, args):
241 from setenv import add_path
242 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
243 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
244 add_path(path, "PYTHONPATH")
245 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
246 add_path(path, "PYTHONPATH")
251 command, options = self.__parseArguments(args)
255 if args and args[0] in ["-h","--help","help"]:
258 # try to default to "start" command
259 command = "_runAppli"
262 res = getattr(self, command)(options) # run appropriate method
263 return res or (None, None)
264 except SystemExit, returncode:
266 self.getLogger().error("SystemExit %s in method %s.", returncode, command)
268 except StandardError:
269 self.getLogger().error("Unexpected error:")
271 traceback.print_exc()
273 except SalomeContextException, e:
274 self.getLogger().error(e)
278 def __setContextFromConfigFile(self, filename, reserved=None):
282 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
283 except SalomeContextException, e:
285 self.getLogger().error(msg)
289 for var in unsetVars:
290 self.unsetVariable(var)
293 for reserved in reservedDict:
294 a = filter(None, reservedDict[reserved]) # remove empty elements
295 a = [ os.path.realpath(x) for x in a ]
296 reformattedVals = os.pathsep.join(a)
297 if reserved in ["INCLUDE", "LIBPATH"]:
298 self.addToVariable(reserved, reformattedVals, separator=' ')
300 self.addToVariable(reserved, reformattedVals)
303 for key,val in configVars:
304 self.setVariable(key, val, overwrite=True)
307 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
308 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
309 sys.path[:0] = pythonpath
312 def _runAppli(self, args=None):
315 # Initialize SALOME environment
316 sys.argv = ['runSalome'] + args
318 setenv.main(True, exeName="salome start")
321 runSalome.runSalome()
324 def _setContext(self, args=None):
325 salome_context_set = os.getenv("SALOME_CONTEXT_SET")
326 if salome_context_set:
328 print "*** SALOME context has already been set."
329 print "*** Enter 'exit' (only once!) to leave SALOME context."
333 os.environ["SALOME_CONTEXT_SET"] = "yes"
335 print "*** SALOME context is now set."
336 print "*** Enter 'exit' (only once!) to leave SALOME context."
340 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
341 return proc.communicate()
344 def _runSession(self, args=None):
347 sys.argv = ['runSession'] + args
349 params, args = runSession.configureSession(args, exe="salome shell")
351 sys.argv = ['runSession'] + args
355 return runSession.runSession(params, args)
358 def _runConsole(self, args=None):
361 # Initialize SALOME environment
362 sys.argv = ['runConsole']
367 return runConsole.connect(args)
370 def _kill(self, args=None):
375 print "Port number(s) not provided to command: salome kill <port(s)>"
378 from multiprocessing import Process
379 from killSalomeWithPort import killMyPort
382 with tempfile.NamedTemporaryFile():
383 p = Process(target = killMyPort, args=(port,))
389 def _killAll(self, unused=None):
391 import PortManager # mandatory
392 from multiprocessing import Process
393 from killSalomeWithPort import killMyPort
394 ports = PortManager.getBusyPorts()
398 for port,owner in ports:
400 with tempfile.NamedTemporaryFile():
401 p = Process(target = killMyPort, args=(port,))
405 # :TODO: should be declared obsolete
406 from killSalome import killAllPorts
411 def _runTests(self, args=None):
414 sys.argv = ['runTests']
419 return runTests.runTests(args, exe="salome test")
422 def _showSoftwareVersions(self, softwares=None):
423 config = ConfigParser.SafeConfigParser()
424 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
425 filename = os.path.join(absoluteAppliPath, "sha1_collections.txt")
428 with open(filename) as f:
431 software, version, sha1 = line.split()
432 versions[software.upper()] = version
433 if len(software) > max_len:
434 max_len = len(software)
440 for soft in softwares:
441 if versions.has_key(soft.upper()):
442 print soft.upper().rjust(max_len), versions[soft.upper()]
445 od = collections.OrderedDict(sorted(versions.items()))
446 for name, version in od.iteritems():
447 print name.rjust(max_len), versions[name]
450 def _showInfo(self, args=None):
454 usage = "Usage: salome info [options]"
456 Display some information about SALOME.\n
457 Available options are:
458 -p,--ports Show the list of busy ports (running SALOME instances).
459 -s,--softwares [software(s)] Show the list and versions of SALOME softwares.
460 Software names must be separated by blank characters.
461 If no software is given, show version of all softwares.
462 -v,--version Show running SALOME version.
463 -h,--help Show this message.
468 if "-h" in args or "--help" in args:
472 if "-p" in args or "--ports" in args:
474 ports = PortManager.getBusyPorts()
475 print "SALOME instances are running on ports:", ports
477 print "Last started instance on port %s"%ports[-1][0]
479 if "-s" in args or "--softwares" in args:
481 index = args.index("-s")
483 index = args.index("--softwares")
485 while indexEnd < len(args) and args[indexEnd][0] != "-":
486 indexEnd = indexEnd + 1
487 self._showSoftwareVersions(softwares=args[index+1:indexEnd])
489 if "-v" in args or "--version" in args:
490 print "Running with python", platform.python_version()
491 self._runAppli(["--version"])
494 def _showDoc(self, args=None):
500 print "Module(s) not provided to command: salome doc <module(s)>"
503 appliPath = os.getenv("ABSOLUTE_APPLI_PATH")
505 raise SalomeContextException("Unable to find application path. Please check that the variable ABSOLUTE_APPLI_PATH is set.")
506 baseDir = os.path.join(appliPath, "share", "doc", "salome")
507 for module in modules:
508 docfile = os.path.join(baseDir, "gui", module.upper(), "index.html")
509 if not os.path.isfile(docfile):
510 docfile = os.path.join(baseDir, "tui", module.upper(), "index.html")
511 if not os.path.isfile(docfile):
512 docfile = os.path.join(baseDir, "dev", module.upper(), "index.html")
513 if os.path.isfile(docfile):
514 out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
516 print "Online documentation is not accessible for module:", module
518 def _usage(self, unused=None):
522 def _makeCoffee(self, unused=None):
525 print " ___...(-------)-....___"
526 print " .-\"\" ) ( \"\"-."
527 print " .-\'``\'|-._ ) _.-|"
528 print " / .--.| `\"\"---...........---\"\"` |"
533 print " `\\ `| SALOME |"
534 print " _/ /\\ 4 EVER /"
535 print " (__/ \\ <3 /"
536 print " _..---\"\"` \\ /`\"\"---.._"
537 print " .-\' \\ / \'-."
538 print " : `-.__ __.-\' :"
539 print " : ) \"\"---...---\"\" ( :"
540 print " \'._ `\"--...___...--\"` _.\'"
541 print " \\\"\"--..__ __..--\"\"/"
542 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
543 print " `\"\"--..,,_____ _____,,..--\"\"`"
544 print " `\"\"\"----\"\"\"`"
546 print " SALOME is working for you; what else?"
551 def _getCar(self, unused=None):
552 print " _____________"
553 print " ..---:::::::-----------. ::::;;."
554 print " .\'\"\"\"\"\"\" ;; \\ \":."
555 print " .\'\' ; \\ \"\\__."
556 print " .\' ;; ; \\\\\";"
557 print " .\' ; _____; \\\\/"
558 print " .\' :; ;\" \\ ___:\'."
559 print " .\'--........................... : = ____:\" \\ \\"
560 print " ..-\"\" \"\"\"\' o\"\"\" ; ; :"
561 print " .--\"\" .----- ..----... _.- --. ..-\" ; ; ; ;"
562 print " .\"\"_- \"--\"\"-----\'\"\" _-\" .-\"\" ; ; .-."
563 print " .\' .\' SALOME .\" .\" ; ; /. |"
564 print " /-./\' 4 EVER <3 .\" / _.. ; ; ;;;|"
565 print " : ;-.______ / _________==. /_ \\ ; ; ;;;;"
566 print " ; / | \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\" : /\" \". |; ; _; ;;;"
567 print " /\"-/ | / / / / ;|; ;-\" | ;\';"
568 print ":- : \"\"\"----______ / / ____. . .\"\'. ;; .-\"..T\" ."
569 print "\'. \" ___ \"\": \'\"\"\"\"\"\"\"\"\"\"\"\"\"\" . ; ; ;; ;.\" .\" \'--\""
570 print " \", __ \"\"\" \"\"---... :- - - - - - - - - \' \' ; ; ; ;;\" .\""
571 print " /. ; \"\"\"---___ ; ; ; ;|.\"\""
572 print " : \": \"\"\"----. .-------. ; ; ; ;:"
573 print " \\ \'--__ \\ \\ \\ / | ; ;;"
574 print " \'-.. \"\"\"\"---___ : .______..\\ __/..-\"\"| ; ; ;"
575 print " \"\"--.. \"\"\"--\" m l s . \". . ;"
576 print " \"\"------... ..--\"\" \" :"
577 print " \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" \\ /"
580 print " Drive your simulation properly with SALOME!"
585 # Add the following two methods since logger is not pickable
586 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
587 def __getstate__(self):
588 d = dict(self.__dict__)
589 if hasattr(self, '_logger'):
593 def __setstate__(self, d):
594 self.__dict__.update(d) # I *think* this is a safe way to do it
596 # Excluding self._logger from pickle operation imply using the following method to access logger
598 if not hasattr(self, '_logger'):
599 self._logger = logging.getLogger(__name__)
600 #self._logger.setLevel(logging.DEBUG)
601 #self._logger.setLevel(logging.WARNING)
602 self._logger.setLevel(logging.ERROR)
606 if __name__ == "__main__":
607 if len(sys.argv) == 3:
608 context = pickle.loads(sys.argv[1])
609 args = pickle.loads(sys.argv[2])
611 (out, err) = context._startSalome(args)
613 sys.stdout.write(out)
615 sys.stderr.write(err)