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', 'QT_PLUGIN_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 __loadEnvModules(self, env_modules):
104 print("Trying to load env modules: %s..." % ' '.join(env_modules))
106 out, err = subprocess.Popen(["modulecmd", "python", "load"] + env_modules, 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 env_modules_option = "--with-env-modules="
119 env_modules_l = [x for x in args if x.startswith(env_modules_option)]
121 env_modules = env_modules_l[-1][len(env_modules_option):].split(',')
122 self.__loadEnvModules(env_modules)
123 args = [x for x in args if not x.startswith(env_modules_option)]
125 env_modules = os.getenv("SALOME_ENV_MODULES", None)
127 self.__loadEnvModules(env_modules.split(','))
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
264 except SystemExit as ex:
266 self.getLogger().error("SystemExit %s in method %s.", ex.code, 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()
325 def _setContext(self, args=None):
326 salome_context_set = os.getenv("SALOME_CONTEXT_SET")
327 if salome_context_set:
329 print "*** SALOME context has already been set."
330 print "*** Enter 'exit' (only once!) to leave SALOME context."
334 os.environ["SALOME_CONTEXT_SET"] = "yes"
336 print "*** SALOME context is now set."
337 print "*** Enter 'exit' (only once!) to leave SALOME context."
341 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
343 return proc.returncode
346 def _runSession(self, args=None):
349 sys.argv = ['runSession'] + args
351 params, args = runSession.configureSession(args, exe="salome shell")
353 sys.argv = ['runSession'] + args
357 return runSession.runSession(params, args)
360 def _runConsole(self, args=None):
363 # Initialize SALOME environment
364 sys.argv = ['runConsole']
369 return runConsole.connect(args)
372 def _kill(self, args=None):
377 print "Port number(s) not provided to command: salome kill <port(s)>"
380 from multiprocessing import Process
381 from killSalomeWithPort import killMyPort
384 with tempfile.NamedTemporaryFile():
385 p = Process(target = killMyPort, args=(port,))
391 def _killAll(self, unused=None):
393 import PortManager # mandatory
394 from multiprocessing import Process
395 from killSalomeWithPort import killMyPort
396 ports = PortManager.getBusyPorts()['this']
401 with tempfile.NamedTemporaryFile():
402 p = Process(target = killMyPort, args=(port,))
406 # :TODO: should be declared obsolete
407 from killSalome import killAllPorts
413 def _runTests(self, args=None):
416 sys.argv = ['runTests']
421 return runTests.runTests(args, exe="salome test")
424 def _showSoftwareVersions(self, softwares=None):
425 config = ConfigParser.SafeConfigParser()
426 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
427 filename = os.path.join(absoluteAppliPath, "sha1_collections.txt")
430 with open(filename) as f:
433 software, version, sha1 = line.split()
434 versions[software.upper()] = version
435 if len(software) > max_len:
436 max_len = len(software)
442 for soft in softwares:
443 if versions.has_key(soft.upper()):
444 print soft.upper().rjust(max_len), versions[soft.upper()]
447 od = collections.OrderedDict(sorted(versions.items()))
448 for name, version in od.iteritems():
449 print name.rjust(max_len), versions[name]
452 def _showInfo(self, args=None):
456 usage = "Usage: salome info [options]"
458 Display some information about SALOME.\n
459 Available options are:
460 -p,--ports Show the list of busy ports (running SALOME instances).
461 -s,--softwares [software(s)] Show the list and versions of SALOME softwares.
462 Software names must be separated by blank characters.
463 If no software is given, show version of all softwares.
464 -v,--version Show running SALOME version.
465 -h,--help Show this message.
470 if "-h" in args or "--help" in args:
474 if "-p" in args or "--ports" in args:
476 ports = PortManager.getBusyPorts()
477 this_ports = ports['this']
478 other_ports = ports['other']
479 if this_ports or other_ports:
480 print "SALOME instances are running on the following ports:"
482 print " This application:", this_ports
484 print " No SALOME instances of this application"
486 print " Other applications:", other_ports
488 print " No SALOME instances of other applications"
490 print "No SALOME instances are running"
492 if "-s" in args or "--softwares" in args:
494 index = args.index("-s")
496 index = args.index("--softwares")
498 while indexEnd < len(args) and args[indexEnd][0] != "-":
499 indexEnd = indexEnd + 1
500 self._showSoftwareVersions(softwares=args[index+1:indexEnd])
502 if "-v" in args or "--version" in args:
503 print "Running with python", platform.python_version()
504 return self._runAppli(["--version"])
509 def _showDoc(self, args=None):
515 print "Module(s) not provided to command: salome doc <module(s)>"
518 appliPath = os.getenv("ABSOLUTE_APPLI_PATH")
520 raise SalomeContextException("Unable to find application path. Please check that the variable ABSOLUTE_APPLI_PATH is set.")
521 baseDir = os.path.join(appliPath, "share", "doc", "salome")
522 for module in modules:
523 docfile = os.path.join(baseDir, "gui", module.upper(), "index.html")
524 if not os.path.isfile(docfile):
525 docfile = os.path.join(baseDir, "tui", module.upper(), "index.html")
526 if not os.path.isfile(docfile):
527 docfile = os.path.join(baseDir, "dev", module.upper(), "index.html")
528 if os.path.isfile(docfile):
529 out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
531 print "Online documentation is not accessible for module:", module
533 def _usage(self, unused=None):
537 def _makeCoffee(self, unused=None):
540 print " ___...(-------)-....___"
541 print " .-\"\" ) ( \"\"-."
542 print " .-\'``\'|-._ ) _.-|"
543 print " / .--.| `\"\"---...........---\"\"` |"
548 print " `\\ `| SALOME |"
549 print " _/ /\\ 4 EVER /"
550 print " (__/ \\ <3 /"
551 print " _..---\"\"` \\ /`\"\"---.._"
552 print " .-\' \\ / \'-."
553 print " : `-.__ __.-\' :"
554 print " : ) \"\"---...---\"\" ( :"
555 print " \'._ `\"--...___...--\"` _.\'"
556 print " \\\"\"--..__ __..--\"\"/"
557 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
558 print " `\"\"--..,,_____ _____,,..--\"\"`"
559 print " `\"\"\"----\"\"\"`"
561 print " SALOME is working for you; what else?"
565 def _getCar(self, unused=None):
566 print " _____________"
567 print " ..---:::::::-----------. ::::;;."
568 print " .\'\"\"\"\"\"\" ;; \\ \":."
569 print " .\'\' ; \\ \"\\__."
570 print " .\' ;; ; \\\\\";"
571 print " .\' ; _____; \\\\/"
572 print " .\' :; ;\" \\ ___:\'."
573 print " .\'--........................... : = ____:\" \\ \\"
574 print " ..-\"\" \"\"\"\' o\"\"\" ; ; :"
575 print " .--\"\" .----- ..----... _.- --. ..-\" ; ; ; ;"
576 print " .\"\"_- \"--\"\"-----\'\"\" _-\" .-\"\" ; ; .-."
577 print " .\' .\' SALOME .\" .\" ; ; /. |"
578 print " /-./\' 4 EVER <3 .\" / _.. ; ; ;;;|"
579 print " : ;-.______ / _________==. /_ \\ ; ; ;;;;"
580 print " ; / | \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\" : /\" \". |; ; _; ;;;"
581 print " /\"-/ | / / / / ;|; ;-\" | ;\';"
582 print ":- : \"\"\"----______ / / ____. . .\"\'. ;; .-\"..T\" ."
583 print "\'. \" ___ \"\": \'\"\"\"\"\"\"\"\"\"\"\"\"\"\" . ; ; ;; ;.\" .\" \'--\""
584 print " \", __ \"\"\" \"\"---... :- - - - - - - - - \' \' ; ; ; ;;\" .\""
585 print " /. ; \"\"\"---___ ; ; ; ;|.\"\""
586 print " : \": \"\"\"----. .-------. ; ; ; ;:"
587 print " \\ \'--__ \\ \\ \\ / | ; ;;"
588 print " \'-.. \"\"\"\"---___ : .______..\\ __/..-\"\"| ; ; ;"
589 print " \"\"--.. \"\"\"--\" m l s . \". . ;"
590 print " \"\"------... ..--\"\" \" :"
591 print " \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" \\ /"
594 print " Drive your simulation properly with SALOME!"
598 # Add the following two methods since logger is not pickable
599 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
600 def __getstate__(self):
601 d = dict(self.__dict__)
602 if hasattr(self, '_logger'):
606 def __setstate__(self, d):
607 self.__dict__.update(d) # I *think* this is a safe way to do it
609 # Excluding self._logger from pickle operation imply using the following method to access logger
611 if not hasattr(self, '_logger'):
612 self._logger = logging.getLogger(__name__)
613 #self._logger.setLevel(logging.DEBUG)
614 #self._logger.setLevel(logging.WARNING)
615 self._logger.setLevel(logging.ERROR)
619 if __name__ == "__main__":
620 if len(sys.argv) == 3:
621 context = pickle.loads(sys.argv[1])
622 args = pickle.loads(sys.argv[2])
624 status = context._startSalome(args)