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 Starts a SALOME session (through virtual application)
42 context Initializes SALOME context.
43 shell Initializes SALOME context, and executes scripts passed
45 connect Connects a Python console to the active SALOME session
46 kill <port(s)> Terminate SALOME session running on given ports for current user
47 Port numbers must be separated by blank characters
48 killall Kill *all* SALOME running sessions for current user
49 test Run SALOME tests.
50 info Display some information about SALOME
51 help Show this message
53 If no command is given, default to start.
57 Use salome <command> --help to show help on command ; available for commands:
58 start, shell, connect, test, info.
60 --config=<file,folder,...>
61 ==========================
62 Initialize SALOME context from a list of context files and/or a list
63 of folders containing context files. The list is comma-separated, whithout
71 The SalomeContext class in an API to configure SALOME context then
72 start SALOME using a single python command.
77 Initialize context from a list of configuration files
78 identified by their names.
79 These files should be in appropriate (new .cfg) format.
80 However you can give old .sh environment files; in this case,
81 the SalomeContext class will try to automatically convert them
82 to .cfg format before setting the context.
84 def __init__(self, configFileNames=0):
85 #it could be None explicitely (if user use multiples setVariable...for standalone)
86 if configFileNames is None:
88 configFileNames = configFileNames or []
89 if len(configFileNames) == 0:
90 raise SalomeContextException("No configuration files given")
92 reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_PATH']
93 for filename in configFileNames:
94 basename, extension = os.path.splitext(filename)
95 if extension == ".cfg":
96 self.__setContextFromConfigFile(filename, reserved)
97 elif extension == ".sh":
98 #new convert procedures, temporary could be use not to be automatically deleted
99 #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
100 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
102 convertEnvFileToConfigFile(filename, temp.name, reserved)
103 self.__setContextFromConfigFile(temp.name, reserved)
105 except (ConfigParser.ParsingError, ValueError) as e:
106 self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
110 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
113 def __loadMPI(self, module_name):
114 print "Trying to load MPI module: %s..."%module_name,
116 out, err = subprocess.Popen(["modulecmd", "python", "load", module_name], stdout=subprocess.PIPE).communicate()
117 exec out # define specific environment variables
120 print " ** Failed **"
124 def runSalome(self, args):
126 # Run this module as a script, in order to use appropriate Python interpreter
127 # according to current path (initialized from context files).
128 mpi_module_option = "--with-mpi-module="
129 mpi_module = [x for x in args if x.startswith(mpi_module_option)]
131 mpi_module = mpi_module[0][len(mpi_module_option):]
132 self.__loadMPI(mpi_module)
133 args = [x for x in args if not x.startswith(mpi_module_option)]
135 mpi_module = os.getenv("SALOME_MPI_MODULE_NAME", None)
137 self.__loadMPI(mpi_module)
139 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
140 env_copy = os.environ.copy()
141 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)
142 msg = proc.communicate()
143 return msg, proc.returncode
146 """Append value to PATH environment variable"""
147 def addToPath(self, value):
148 self.addToVariable('PATH', value)
151 """Append value to LD_LIBRARY_PATH environment variable"""
152 def addToLdLibraryPath(self, value):
153 self.addToVariable('LD_LIBRARY_PATH', value)
156 """Append value to DYLD_LIBRARY_PATH environment variable"""
157 def addToDyldLibraryPath(self, value):
158 self.addToVariable('DYLD_LIBRARY_PATH', value)
161 """Append value to PYTHONPATH environment variable"""
162 def addToPythonPath(self, value):
163 self.addToVariable('PYTHONPATH', value)
166 """Set environment variable to value"""
167 def setVariable(self, name, value, overwrite=False):
168 env = os.getenv(name, '')
169 if env and not overwrite:
170 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
174 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
176 value = os.path.expandvars(value) # expand environment variables
177 self.getLogger().debug("Set environment variable: %s=%s", name, value)
178 os.environ[name] = value
181 """Unset environment variable"""
182 def unsetVariable(self, name):
183 if os.environ.has_key(name):
187 """Append value to environment variable"""
188 def addToVariable(self, name, value, separator=os.pathsep):
192 value = os.path.expandvars(value) # expand environment variables
193 self.getLogger().debug("Add to %s: %s", name, value)
194 env = os.getenv(name, None)
196 os.environ[name] = value
198 os.environ[name] = value + separator + env
201 ###################################
202 # This begins the private section #
203 ###################################
205 def __parseArguments(self, args):
206 if len(args) == 0 or args[0].startswith("-"):
212 availableCommands = {
213 'start' : '_runAppli',
214 'context' : '_setContext',
215 'shell' : '_runSession',
216 'connect' : '_runConsole',
218 'killall' : '_killAll',
219 'test' : '_runTests',
220 'info' : '_showInfo',
222 'coffee' : '_makeCoffee',
226 if not command in availableCommands.keys():
230 return availableCommands[command], options
235 Args consist in a mandatory command followed by optionnal parameters.
236 See usage for details on commands.
238 def _startSalome(self, args):
242 from setenv import add_path
243 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
244 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
245 add_path(path, "PYTHONPATH")
246 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
247 add_path(path, "PYTHONPATH")
252 command, options = self.__parseArguments(args)
256 if args and args[0] in ["-h","--help","help"]:
259 # try to default to "start" command
260 command = "_runAppli"
263 res = getattr(self, command)(options) # run appropriate method
264 return res or (None, None)
265 except SystemExit, returncode:
267 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
269 except StandardError:
270 self.getLogger().error("Unexpected error:")
272 traceback.print_exc()
274 except SalomeContextException, e:
275 self.getLogger().error(e)
279 def __setContextFromConfigFile(self, filename, reserved=None):
283 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
284 except SalomeContextException, e:
286 file_dir = os.path.dirname(filename)
287 file_base = os.path.basename(filename)
288 base_no_ext, ext = os.path.splitext(file_base)
289 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
290 if ext == ".cfg" and os.path.isfile(sh_file):
291 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
292 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
294 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
295 self.__setContextFromConfigFile(temp.name, reserved)
297 self.getLogger().warning(msg)
300 except (ConfigParser.ParsingError, ValueError) as e:
301 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
302 self.getLogger().error(msg)
306 self.getLogger().error(msg)
310 for var in unsetVars:
311 self.unsetVariable(var)
314 for reserved in reservedDict:
315 a = filter(None, reservedDict[reserved]) # remove empty elements
316 a = [ os.path.realpath(x) for x in a ]
317 reformattedVals = os.pathsep.join(a)
318 if reserved in ["INCLUDE", "LIBPATH"]:
319 self.addToVariable(reserved, reformattedVals, separator=' ')
321 self.addToVariable(reserved, reformattedVals)
324 for key,val in configVars:
325 self.setVariable(key, val, overwrite=True)
328 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
329 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
330 sys.path[:0] = pythonpath
333 def _runAppli(self, args=None):
336 # Initialize SALOME environment
337 sys.argv = ['runSalome'] + args
342 runSalome.runSalome()
345 def _setContext(self, args=None):
346 salome_context_set = os.getenv("SALOME_CONTEXT_SET")
347 if salome_context_set:
349 print "*** SALOME context has already been set."
350 print "*** Enter 'exit' (only once!) to leave SALOME context."
354 os.environ["SALOME_CONTEXT_SET"] = "yes"
356 print "*** SALOME context is now set."
357 print "*** Enter 'exit' (only once!) to leave SALOME context."
361 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
362 return proc.communicate()
365 def _runSession(self, args=None):
368 sys.argv = ['runSession'] + args
370 params, args = runSession.configureSession(args, exe="salome shell")
372 sys.argv = ['runSession'] + args
376 return runSession.runSession(params, args)
379 def _runConsole(self, args=None):
382 # Initialize SALOME environment
383 sys.argv = ['runConsole']
388 return runConsole.connect(args)
391 def _kill(self, args=None):
396 print "Port number(s) not provided to command: salome kill <port(s)>"
399 from multiprocessing import Process
400 from killSalomeWithPort import killMyPort
403 with tempfile.NamedTemporaryFile():
404 p = Process(target = killMyPort, args=(port,))
410 def _killAll(self, unused=None):
412 import PortManager # mandatory
413 from multiprocessing import Process
414 from killSalomeWithPort import killMyPort
415 ports = PortManager.getBusyPorts()
420 with tempfile.NamedTemporaryFile():
421 p = Process(target = killMyPort, args=(port,))
425 from killSalome import killAllPorts
430 def _runTests(self, args=None):
433 sys.argv = ['runTests']
438 return runTests.runTests(args, exe="salome test")
441 def _showInfo(self, args=None):
445 usage = "Usage: salome info [options]"
447 Display some information about SALOME.\n
448 Available options are:
449 -p,--ports Show list of busy ports (running SALOME instances).
450 -v,--version Show running SALOME version.
451 -h,--help Show this message.
456 if "-h" in args or "--help" in args:
460 if "-p" in args or "--ports" in args:
462 ports = PortManager.getBusyPorts()
463 print "SALOME instances are running on ports:", ports
465 print "Last started instance on port %s"%ports[-1]
467 if "-v" in args or "--version" in args:
468 print "Running with python", platform.python_version()
469 self._runAppli(["--version"])
472 def _usage(self, unused=None):
476 def _makeCoffee(self, unused=None):
479 print " ___...(-------)-....___"
480 print " .-\"\" ) ( \"\"-."
481 print " .-\'``\'|-._ ) _.-|"
482 print " / .--.| `\"\"---...........---\"\"` |"
487 print " `\\ `| SALOME |"
488 print " _/ /\\ 4 EVER /"
489 print " (__/ \\ <3 /"
490 print " _..---\"\"` \\ /`\"\"---.._"
491 print " .-\' \\ / \'-."
492 print " : `-.__ __.-\' :"
493 print " : ) \"\"---...---\"\" ( :"
494 print " \'._ `\"--...___...--\"` _.\'"
495 print " \\\"\"--..__ __..--\"\"/"
496 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
497 print " `\"\"--..,,_____ _____,,..--\"\"`"
498 print " `\"\"\"----\"\"\"`"
500 print " SALOME is working for you; what else?"
505 def _getCar(self, unused=None):
506 print " _____________"
507 print " ..---:::::::-----------. ::::;;."
508 print " .\'\"\"\"\"\"\" ;; \\ \":."
509 print " .\'\' ; \\ \"\\__."
510 print " .\' ;; ; \\\\\";"
511 print " .\' ; _____; \\\\/"
512 print " .\' :; ;\" \\ ___:\'."
513 print " .\'--........................... : = ____:\" \\ \\"
514 print " ..-\"\" \"\"\"\' o\"\"\" ; ; :"
515 print " .--\"\" .----- ..----... _.- --. ..-\" ; ; ; ;"
516 print " .\"\"_- \"--\"\"-----\'\"\" _-\" .-\"\" ; ; .-."
517 print " .\' .\' SALOME .\" .\" ; ; /. |"
518 print " /-./\' 4 EVER <3 .\" / _.. ; ; ;;;|"
519 print " : ;-.______ / _________==. /_ \\ ; ; ;;;;"
520 print " ; / | \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\" : /\" \". |; ; _; ;;;"
521 print " /\"-/ | / / / / ;|; ;-\" | ;\';"
522 print ":- : \"\"\"----______ / / ____. . .\"\'. ;; .-\"..T\" ."
523 print "\'. \" ___ \"\": \'\"\"\"\"\"\"\"\"\"\"\"\"\"\" . ; ; ;; ;.\" .\" \'--\""
524 print " \", __ \"\"\" \"\"---... :- - - - - - - - - \' \' ; ; ; ;;\" .\""
525 print " /. ; \"\"\"---___ ; ; ; ;|.\"\""
526 print " : \": \"\"\"----. .-------. ; ; ; ;:"
527 print " \\ \'--__ \\ \\ \\ / | ; ;;"
528 print " \'-.. \"\"\"\"---___ : .______..\\ __/..-\"\"| ; ; ;"
529 print " \"\"--.. \"\"\"--\" m l s . \". . ;"
530 print " \"\"------... ..--\"\" \" :"
531 print " \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" \\ /"
534 print " Drive your simulation properly with SALOME!"
539 # Add the following two methods since logger is not pickable
540 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
541 def __getstate__(self):
542 d = dict(self.__dict__)
543 if hasattr(self, '_logger'):
547 def __setstate__(self, d):
548 self.__dict__.update(d) # I *think* this is a safe way to do it
550 # Excluding self._logger from pickle operation imply using the following method to access logger
552 if not hasattr(self, '_logger'):
553 self._logger = logging.getLogger(__name__)
554 #self._logger.setLevel(logging.DEBUG)
555 #self._logger.setLevel(logging.WARNING)
556 self._logger.setLevel(logging.ERROR)
560 if __name__ == "__main__":
561 if len(sys.argv) == 3:
562 context = pickle.loads(sys.argv[1])
563 args = pickle.loads(sys.argv[2])
565 (out, err) = context._startSalome(args)
567 sys.stdout.write(out)
569 sys.stderr.write(err)