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()['this']
399 with tempfile.NamedTemporaryFile():
400 p = Process(target = killMyPort, args=(port,))
404 # :TODO: should be declared obsolete
405 from killSalome import killAllPorts
410 def _runTests(self, args=None):
413 sys.argv = ['runTests']
418 return runTests.runTests(args, exe="salome test")
421 def _showSoftwareVersions(self, softwares=None):
422 config = ConfigParser.SafeConfigParser()
423 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
424 filename = os.path.join(absoluteAppliPath, "sha1_collections.txt")
427 with open(filename) as f:
430 software, version, sha1 = line.split()
431 versions[software.upper()] = version
432 if len(software) > max_len:
433 max_len = len(software)
439 for soft in softwares:
440 if versions.has_key(soft.upper()):
441 print soft.upper().rjust(max_len), versions[soft.upper()]
444 od = collections.OrderedDict(sorted(versions.items()))
445 for name, version in od.iteritems():
446 print name.rjust(max_len), versions[name]
449 def _showInfo(self, args=None):
453 usage = "Usage: salome info [options]"
455 Display some information about SALOME.\n
456 Available options are:
457 -p,--ports Show the list of busy ports (running SALOME instances).
458 -s,--softwares [software(s)] Show the list and versions of SALOME softwares.
459 Software names must be separated by blank characters.
460 If no software is given, show version of all softwares.
461 -v,--version Show running SALOME version.
462 -h,--help Show this message.
467 if "-h" in args or "--help" in args:
471 if "-p" in args or "--ports" in args:
473 ports = PortManager.getBusyPorts()
474 this_ports = ports['this']
475 other_ports = ports['other']
476 if this_ports or other_ports:
477 print "SALOME instances are running on the following ports:"
479 print " This application:", this_ports
481 print " No SALOME instances of this application"
483 print " Other applications:", other_ports
485 print " No SALOME instances of other applications"
487 print "No SALOME instances are running"
489 if "-s" in args or "--softwares" in args:
491 index = args.index("-s")
493 index = args.index("--softwares")
495 while indexEnd < len(args) and args[indexEnd][0] != "-":
496 indexEnd = indexEnd + 1
497 self._showSoftwareVersions(softwares=args[index+1:indexEnd])
499 if "-v" in args or "--version" in args:
500 print "Running with python", platform.python_version()
501 self._runAppli(["--version"])
504 def _showDoc(self, args=None):
510 print "Module(s) not provided to command: salome doc <module(s)>"
513 appliPath = os.getenv("ABSOLUTE_APPLI_PATH")
515 raise SalomeContextException("Unable to find application path. Please check that the variable ABSOLUTE_APPLI_PATH is set.")
516 baseDir = os.path.join(appliPath, "share", "doc", "salome")
517 for module in modules:
518 docfile = os.path.join(baseDir, "gui", module.upper(), "index.html")
519 if not os.path.isfile(docfile):
520 docfile = os.path.join(baseDir, "tui", module.upper(), "index.html")
521 if not os.path.isfile(docfile):
522 docfile = os.path.join(baseDir, "dev", module.upper(), "index.html")
523 if os.path.isfile(docfile):
524 out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
526 print "Online documentation is not accessible for module:", module
528 def _usage(self, unused=None):
532 def _makeCoffee(self, unused=None):
535 print " ___...(-------)-....___"
536 print " .-\"\" ) ( \"\"-."
537 print " .-\'``\'|-._ ) _.-|"
538 print " / .--.| `\"\"---...........---\"\"` |"
543 print " `\\ `| SALOME |"
544 print " _/ /\\ 4 EVER /"
545 print " (__/ \\ <3 /"
546 print " _..---\"\"` \\ /`\"\"---.._"
547 print " .-\' \\ / \'-."
548 print " : `-.__ __.-\' :"
549 print " : ) \"\"---...---\"\" ( :"
550 print " \'._ `\"--...___...--\"` _.\'"
551 print " \\\"\"--..__ __..--\"\"/"
552 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
553 print " `\"\"--..,,_____ _____,,..--\"\"`"
554 print " `\"\"\"----\"\"\"`"
556 print " SALOME is working for you; what else?"
561 def _getCar(self, unused=None):
562 print " _____________"
563 print " ..---:::::::-----------. ::::;;."
564 print " .\'\"\"\"\"\"\" ;; \\ \":."
565 print " .\'\' ; \\ \"\\__."
566 print " .\' ;; ; \\\\\";"
567 print " .\' ; _____; \\\\/"
568 print " .\' :; ;\" \\ ___:\'."
569 print " .\'--........................... : = ____:\" \\ \\"
570 print " ..-\"\" \"\"\"\' o\"\"\" ; ; :"
571 print " .--\"\" .----- ..----... _.- --. ..-\" ; ; ; ;"
572 print " .\"\"_- \"--\"\"-----\'\"\" _-\" .-\"\" ; ; .-."
573 print " .\' .\' SALOME .\" .\" ; ; /. |"
574 print " /-./\' 4 EVER <3 .\" / _.. ; ; ;;;|"
575 print " : ;-.______ / _________==. /_ \\ ; ; ;;;;"
576 print " ; / | \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\" : /\" \". |; ; _; ;;;"
577 print " /\"-/ | / / / / ;|; ;-\" | ;\';"
578 print ":- : \"\"\"----______ / / ____. . .\"\'. ;; .-\"..T\" ."
579 print "\'. \" ___ \"\": \'\"\"\"\"\"\"\"\"\"\"\"\"\"\" . ; ; ;; ;.\" .\" \'--\""
580 print " \", __ \"\"\" \"\"---... :- - - - - - - - - \' \' ; ; ; ;;\" .\""
581 print " /. ; \"\"\"---___ ; ; ; ;|.\"\""
582 print " : \": \"\"\"----. .-------. ; ; ; ;:"
583 print " \\ \'--__ \\ \\ \\ / | ; ;;"
584 print " \'-.. \"\"\"\"---___ : .______..\\ __/..-\"\"| ; ; ;"
585 print " \"\"--.. \"\"\"--\" m l s . \". . ;"
586 print " \"\"------... ..--\"\" \" :"
587 print " \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" \\ /"
590 print " Drive your simulation properly with SALOME!"
595 # Add the following two methods since logger is not pickable
596 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
597 def __getstate__(self):
598 d = dict(self.__dict__)
599 if hasattr(self, '_logger'):
603 def __setstate__(self, d):
604 self.__dict__.update(d) # I *think* this is a safe way to do it
606 # Excluding self._logger from pickle operation imply using the following method to access logger
608 if not hasattr(self, '_logger'):
609 self._logger = logging.getLogger(__name__)
610 #self._logger.setLevel(logging.DEBUG)
611 #self._logger.setLevel(logging.WARNING)
612 self._logger.setLevel(logging.ERROR)
616 if __name__ == "__main__":
617 if len(sys.argv) == 3:
618 context = pickle.loads(sys.argv[1])
619 args = pickle.loads(sys.argv[2])
621 (out, err) = context._startSalome(args)
623 sys.stdout.write(out)
625 sys.stderr.write(err)