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
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_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_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().warning("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)
146 self.addToVariable('LD_LIBRARY_PATH', value)
149 """Append value to DYLD_LIBRARY_PATH environment variable"""
150 def addToDyldLibraryPath(self, value):
151 self.addToVariable('DYLD_LIBRARY_PATH', value)
154 """Append value to PYTHONPATH environment variable"""
155 def addToPythonPath(self, value):
156 self.addToVariable('PYTHONPATH', value)
159 """Set environment variable to value"""
160 def setVariable(self, name, value, overwrite=False):
161 env = os.getenv(name, '')
162 if env and not overwrite:
163 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
167 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
169 value = os.path.expandvars(value) # expand environment variables
170 self.getLogger().debug("Set environment variable: %s=%s", name, value)
171 os.environ[name] = value
174 """Unset environment variable"""
175 def unsetVariable(self, name):
176 if os.environ.has_key(name):
180 """Append value to environment variable"""
181 def addToVariable(self, name, value, separator=os.pathsep):
185 value = os.path.expandvars(value) # expand environment variables
186 self.getLogger().debug("Add to %s: %s", name, value)
187 env = os.getenv(name, None)
189 os.environ[name] = value
191 os.environ[name] = value + separator + env
194 ###################################
195 # This begins the private section #
196 ###################################
198 def __parseArguments(self, args):
199 if len(args) == 0 or args[0].startswith("-"):
205 availableCommands = {
206 'start' : '_runAppli',
207 'context' : '_setContext',
208 'shell' : '_runSession',
209 'connect' : '_runConsole',
211 'killall' : '_killAll',
212 'test' : '_runTests',
213 'info' : '_showInfo',
216 'coffee' : '_makeCoffee',
220 if not command in availableCommands.keys():
224 return availableCommands[command], options
229 Args consist in a mandatory command followed by optionnal parameters.
230 See usage for details on commands.
232 def _startSalome(self, args):
236 from setenv import add_path
237 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
238 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
239 add_path(path, "PYTHONPATH")
240 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
241 add_path(path, "PYTHONPATH")
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 self.getLogger().error(msg)
284 for var in unsetVars:
285 self.unsetVariable(var)
288 for reserved in reservedDict:
289 a = filter(None, reservedDict[reserved]) # remove empty elements
290 a = [ os.path.realpath(x) for x in a ]
291 reformattedVals = os.pathsep.join(a)
292 if reserved in ["INCLUDE", "LIBPATH"]:
293 self.addToVariable(reserved, reformattedVals, separator=' ')
295 self.addToVariable(reserved, reformattedVals)
298 for key,val in configVars:
299 self.setVariable(key, val, overwrite=True)
302 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
303 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
304 sys.path[:0] = pythonpath
307 def _runAppli(self, args=None):
310 # Initialize SALOME environment
311 sys.argv = ['runSalome'] + args
313 setenv.main(True, exeName="salome start")
316 runSalome.runSalome()
319 def _setContext(self, args=None):
320 salome_context_set = os.getenv("SALOME_CONTEXT_SET")
321 if salome_context_set:
323 print "*** SALOME context has already been set."
324 print "*** Enter 'exit' (only once!) to leave SALOME context."
328 os.environ["SALOME_CONTEXT_SET"] = "yes"
330 print "*** SALOME context is now set."
331 print "*** Enter 'exit' (only once!) to leave SALOME context."
335 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
336 return proc.communicate()
339 def _runSession(self, args=None):
342 sys.argv = ['runSession'] + args
344 params, args = runSession.configureSession(args, exe="salome shell")
346 sys.argv = ['runSession'] + args
350 return runSession.runSession(params, args)
353 def _runConsole(self, args=None):
356 # Initialize SALOME environment
357 sys.argv = ['runConsole']
362 return runConsole.connect(args)
365 def _kill(self, args=None):
370 print "Port number(s) not provided to command: salome kill <port(s)>"
373 from multiprocessing import Process
374 from killSalomeWithPort import killMyPort
377 with tempfile.NamedTemporaryFile():
378 p = Process(target = killMyPort, args=(port,))
384 def _killAll(self, unused=None):
386 import PortManager # mandatory
387 from multiprocessing import Process
388 from killSalomeWithPort import killMyPort
389 ports = PortManager.getBusyPorts()
394 with tempfile.NamedTemporaryFile():
395 p = Process(target = killMyPort, args=(port,))
399 # :TODO: should be declared obsolete
400 from killSalome import killAllPorts
405 def _runTests(self, args=None):
408 sys.argv = ['runTests']
413 return runTests.runTests(args, exe="salome test")
416 def _showSoftwareVersions(self, softwares=None):
417 config = ConfigParser.SafeConfigParser()
418 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
419 filename = os.path.join(absoluteAppliPath, "sha1_collections.txt")
422 with open(filename) as f:
425 software, version, sha1 = line.split()
426 versions[software.upper()] = version
427 if len(software) > max_len:
428 max_len = len(software)
434 for soft in softwares:
435 if versions.has_key(soft.upper()):
436 print soft.upper().rjust(max_len), versions[soft.upper()]
438 for name, version in versions.items():
439 print name.rjust(max_len), versions[name]
442 def _showInfo(self, args=None):
446 usage = "Usage: salome info [options]"
448 Display some information about SALOME.\n
449 Available options are:
450 -p,--ports Show the list of busy ports (running SALOME instances).
451 -s,--softwares Show the list and versions of SALOME softwares.
452 -v,--version Show running SALOME version.
453 -h,--help Show this message.
458 if "-h" in args or "--help" in args:
462 if "-p" in args or "--ports" in args:
464 ports = PortManager.getBusyPorts()
465 print "SALOME instances are running on ports:", ports
467 print "Last started instance on port %s"%ports[-1]
469 if "-s" in args or "--softwares" in args:
470 self._showSoftwareVersions()
472 if "-v" in args or "--version" in args:
473 print "Running with python", platform.python_version()
474 self._runAppli(["--version"])
477 def _showDoc(self, args=None):
483 print "Module(s) not provided to command: salome doc <module(s)>"
486 appliPath = os.getenv("ABSOLUTE_APPLI_PATH")
488 raise SalomeContextException("Unable to find application path. Please check that the variable ABSOLUTE_APPLI_PATH is set.")
489 baseDir = os.path.join(appliPath, "share", "doc", "salome")
490 for module in modules:
491 docfile = os.path.join(baseDir, "gui", module.upper(), "index.html")
492 if not os.path.isfile(docfile):
493 docfile = os.path.join(baseDir, "tui", module.upper(), "index.html")
494 if not os.path.isfile(docfile):
495 docfile = os.path.join(baseDir, "dev", module.upper(), "index.html")
496 if os.path.isfile(docfile):
497 out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
499 print "Online documentation is not accessible for module:", module
501 def _usage(self, unused=None):
505 def _makeCoffee(self, unused=None):
508 print " ___...(-------)-....___"
509 print " .-\"\" ) ( \"\"-."
510 print " .-\'``\'|-._ ) _.-|"
511 print " / .--.| `\"\"---...........---\"\"` |"
516 print " `\\ `| SALOME |"
517 print " _/ /\\ 4 EVER /"
518 print " (__/ \\ <3 /"
519 print " _..---\"\"` \\ /`\"\"---.._"
520 print " .-\' \\ / \'-."
521 print " : `-.__ __.-\' :"
522 print " : ) \"\"---...---\"\" ( :"
523 print " \'._ `\"--...___...--\"` _.\'"
524 print " \\\"\"--..__ __..--\"\"/"
525 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
526 print " `\"\"--..,,_____ _____,,..--\"\"`"
527 print " `\"\"\"----\"\"\"`"
529 print " SALOME is working for you; what else?"
534 def _getCar(self, unused=None):
535 print " _____________"
536 print " ..---:::::::-----------. ::::;;."
537 print " .\'\"\"\"\"\"\" ;; \\ \":."
538 print " .\'\' ; \\ \"\\__."
539 print " .\' ;; ; \\\\\";"
540 print " .\' ; _____; \\\\/"
541 print " .\' :; ;\" \\ ___:\'."
542 print " .\'--........................... : = ____:\" \\ \\"
543 print " ..-\"\" \"\"\"\' o\"\"\" ; ; :"
544 print " .--\"\" .----- ..----... _.- --. ..-\" ; ; ; ;"
545 print " .\"\"_- \"--\"\"-----\'\"\" _-\" .-\"\" ; ; .-."
546 print " .\' .\' SALOME .\" .\" ; ; /. |"
547 print " /-./\' 4 EVER <3 .\" / _.. ; ; ;;;|"
548 print " : ;-.______ / _________==. /_ \\ ; ; ;;;;"
549 print " ; / | \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\" : /\" \". |; ; _; ;;;"
550 print " /\"-/ | / / / / ;|; ;-\" | ;\';"
551 print ":- : \"\"\"----______ / / ____. . .\"\'. ;; .-\"..T\" ."
552 print "\'. \" ___ \"\": \'\"\"\"\"\"\"\"\"\"\"\"\"\"\" . ; ; ;; ;.\" .\" \'--\""
553 print " \", __ \"\"\" \"\"---... :- - - - - - - - - \' \' ; ; ; ;;\" .\""
554 print " /. ; \"\"\"---___ ; ; ; ;|.\"\""
555 print " : \": \"\"\"----. .-------. ; ; ; ;:"
556 print " \\ \'--__ \\ \\ \\ / | ; ;;"
557 print " \'-.. \"\"\"\"---___ : .______..\\ __/..-\"\"| ; ; ;"
558 print " \"\"--.. \"\"\"--\" m l s . \". . ;"
559 print " \"\"------... ..--\"\" \" :"
560 print " \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" \\ /"
563 print " Drive your simulation properly with SALOME!"
568 # Add the following two methods since logger is not pickable
569 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
570 def __getstate__(self):
571 d = dict(self.__dict__)
572 if hasattr(self, '_logger'):
576 def __setstate__(self, d):
577 self.__dict__.update(d) # I *think* this is a safe way to do it
579 # Excluding self._logger from pickle operation imply using the following method to access logger
581 if not hasattr(self, '_logger'):
582 self._logger = logging.getLogger(__name__)
583 #self._logger.setLevel(logging.DEBUG)
584 #self._logger.setLevel(logging.WARNING)
585 self._logger.setLevel(logging.ERROR)
589 if __name__ == "__main__":
590 if len(sys.argv) == 3:
591 context = pickle.loads(sys.argv[1])
592 args = pickle.loads(sys.argv[2])
594 (out, err) = context._startSalome(args)
596 sys.stdout.write(out)
598 sys.stderr.write(err)