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
36 #exeName = os.path.splitext(os.path.basename(__file__))[0]
39 Usage: salome [command] [options] [--config=<file,folder,...>]
43 start Starts a SALOME session (through virtual application)
44 context Initializes SALOME context.
45 shell Initializes SALOME context, and executes scripts passed
47 connect Connects a Python console to the active SALOME session
48 kill <port(s)> Terminate SALOME session running on given ports for current user
49 Port numbers must be separated by blank characters
50 killall Kill *all* SALOME running sessions for current user
51 test Run SALOME tests.
52 info Display some information about SALOME
53 help Show this message
54 coffee Yes! SALOME can also make coffee!!
56 If no command is given, default to start.
60 Use salome <command> --help to show help on command ; available for commands:
63 --config=<file,folder,...>
64 ==========================
65 Initialize SALOME context from a list of context files and/or a list
66 of folders containing context files. The list is comma-separated, whithout
74 The SalomeContext class in an API to configure SALOME context then
75 start SALOME using a single python command.
80 Initialize context from a list of configuration files
81 identified by their names.
82 These files should be in appropriate (new .cfg) format.
83 However you can give old .sh environment files; in this case,
84 the SalomeContext class will try to automatically convert them
85 to .cfg format before setting the context.
87 def __init__(self, configFileNames=0):
88 #it could be None explicitely (if user use multiples setVariable...for standalone)
89 if configFileNames is None:
91 configFileNames = configFileNames or []
92 if len(configFileNames) == 0:
93 raise SalomeContextException("No configuration files given")
95 reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_PATH']
96 for filename in configFileNames:
97 basename, extension = os.path.splitext(filename)
98 if extension == ".cfg":
99 self.__setContextFromConfigFile(filename, reserved)
100 elif extension == ".sh":
101 #new convert procedures, temporary could be use not to be automatically deleted
102 #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
103 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
105 convertEnvFileToConfigFile(filename, temp.name, reserved)
106 self.__setContextFromConfigFile(temp.name, reserved)
108 except (ConfigParser.ParsingError, ValueError) as e:
109 self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
113 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
116 def __loadMPI(self, module_name):
117 print "Trying to load MPI module: %s..."%module_name,
119 out, err = subprocess.Popen(["modulecmd", "python", "load", module_name], stdout=subprocess.PIPE).communicate()
120 exec out # define specific environment variables
123 print " ** Failed **"
127 def runSalome(self, args):
129 # Run this module as a script, in order to use appropriate Python interpreter
130 # according to current path (initialized from context files).
131 mpi_module_option = "--with-mpi-module="
132 mpi_module = [x for x in args if x.startswith(mpi_module_option)]
134 mpi_module = mpi_module[0][len(mpi_module_option):]
135 self.__loadMPI(mpi_module)
136 args = [x for x in args if not x.startswith(mpi_module_option)]
138 mpi_module = os.getenv("SALOME_MPI_MODULE_NAME", None)
140 self.__loadMPI(mpi_module)
142 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
143 env_copy = os.environ.copy()
144 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)
145 msg = proc.communicate()
146 return msg, proc.returncode
149 """Append value to PATH environment variable"""
150 def addToPath(self, value):
151 self.addToVariable('PATH', value)
154 """Append value to LD_LIBRARY_PATH environment variable"""
155 def addToLdLibraryPath(self, value):
156 self.addToVariable('LD_LIBRARY_PATH', value)
159 """Append value to DYLD_LIBRARY_PATH environment variable"""
160 def addToDyldLibraryPath(self, value):
161 self.addToVariable('DYLD_LIBRARY_PATH', value)
164 """Append value to PYTHONPATH environment variable"""
165 def addToPythonPath(self, value):
166 self.addToVariable('PYTHONPATH', value)
169 """Set environment variable to value"""
170 def setVariable(self, name, value, overwrite=False):
171 env = os.getenv(name, '')
172 if env and not overwrite:
173 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
177 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
179 value = os.path.expandvars(value) # expand environment variables
180 self.getLogger().debug("Set environment variable: %s=%s", name, value)
181 os.environ[name] = value
184 """Unset environment variable"""
185 def unsetVariable(self, name):
186 if os.environ.has_key(name):
190 """Append value to environment variable"""
191 def addToVariable(self, name, value, separator=os.pathsep):
195 value = os.path.expandvars(value) # expand environment variables
196 self.getLogger().debug("Add to %s: %s", name, value)
197 env = os.getenv(name, None)
199 os.environ[name] = value
201 os.environ[name] = value + separator + env
204 ###################################
205 # This begins the private section #
206 ###################################
208 def __parseArguments(self, args):
209 if len(args) == 0 or args[0].startswith("-"):
215 availableCommands = {
216 'start' : '_runAppli',
217 'context' : '_setContext',
218 'shell' : '_runSession',
219 'connect' : '_runConsole',
221 'killall' : '_killAll',
222 'test' : '_runTests',
223 'info' : '_showInfo',
225 'coffee' : '_makeCoffee',
229 if not command in availableCommands.keys():
233 return availableCommands[command], options
238 Args consist in a mandatory command followed by optionnal parameters.
239 See usage for details on commands.
241 def _startSalome(self, args):
244 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
246 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
247 if not path in sys.path:
248 sys.path[:0] = [path]
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=[]):
334 # Initialize SALOME environment
335 sys.argv = ['runSalome'] + args
340 runSalome.runSalome()
343 def _setContext(self, args=None):
344 salome_context_set = os.getenv("SALOME_CONTEXT_SET")
345 if salome_context_set:
347 print "*** SALOME context has already been set."
348 print "*** Enter 'exit' (only once!) to leave SALOME context."
352 os.environ["SALOME_CONTEXT_SET"] = "yes"
354 print "*** SALOME context is now set."
355 print "*** Enter 'exit' (only once!) to leave SALOME context."
359 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
360 return proc.communicate()
363 def _runSession(self, args=[]):
364 sys.argv = ['runSession'] + args
366 params, args = runSession.configureSession(args, exe="salome shell")
368 sys.argv = ['runSession'] + args
372 return runSession.runSession(params, args)
375 def _runConsole(self, args=[]):
376 # Initialize SALOME environment
377 sys.argv = ['runConsole'] + args
381 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
382 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
383 return proc.communicate()
386 def _kill(self, args=[]):
389 print "Port number(s) not provided to command: salome kill <port(s)>"
392 from multiprocessing import Process
393 from killSalomeWithPort import killMyPort
396 with tempfile.NamedTemporaryFile():
397 p = Process(target = killMyPort, args=(port,))
403 def _killAll(self, unused=None):
405 import PortManager # mandatory
406 from multiprocessing import Process
407 from killSalomeWithPort import killMyPort
408 ports = PortManager.getBusyPorts()
413 with tempfile.NamedTemporaryFile():
414 p = Process(target = killMyPort, args=(port,))
418 from killSalome import killAllPorts
423 def _runTests(self, args=[]):
424 sys.argv = ['runTests']
429 return runTests.runTests(args, exe="salome test")
432 def _showInfo(self, unused=None):
433 print "Running with python", platform.python_version()
434 self._runAppli(["--version"])
437 def _usage(self, unused=None):
441 def _makeCoffee(self, unused=None):
444 print " ___...(-------)-....___"
445 print " .-\"\" ) ( \"\"-."
446 print " .-\'``\'|-._ ) _.-|"
447 print " / .--.| `\"\"---...........---\"\"` |"
452 print " `\\ `| SALOME |"
453 print " _/ /\\ 4 EVER /"
454 print " (__/ \\ <3 /"
455 print " _..---\"\"` \\ /`\"\"---.._"
456 print " .-\' \\ / \'-."
457 print " : `-.__ __.-\' :"
458 print " : ) \"\"---...---\"\" ( :"
459 print " \'._ `\"--...___...--\"` _.\'"
460 print " \\\"\"--..__ __..--\"\"/"
461 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
462 print " `\"\"--..,,_____ _____,,..--\"\"`"
463 print " `\"\"\"----\"\"\"`"
465 print " SALOME is working for you; what else?"
470 def _getCar(self, unused=None):
471 print " _____________"
472 print " ..---:::::::-----------. ::::;;."
473 print " .\'\"\"\"\"\"\" ;; \\ \":."
474 print " .\'\' ; \\ \"\\__."
475 print " .\' ;; ; \\\\\";"
476 print " .\' ; _____; \\\\/"
477 print " .\' :; ;\" \\ ___:\'."
478 print " .\'--........................... : = ____:\" \\ \\"
479 print " ..-\"\" \"\"\"\' o\"\"\" ; ; :"
480 print " .--\"\" .----- ..----... _.- --. ..-\" ; ; ; ;"
481 print " .\"\"_- \"--\"\"-----\'\"\" _-\" .-\"\" ; ; .-."
482 print " .\' .\' SALOME .\" .\" ; ; /. |"
483 print " /-./\' 4 EVER <3 .\" / _.. ; ; ;;;|"
484 print " : ;-.______ / _________==. /_ \\ ; ; ;;;;"
485 print " ; / | \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\" : /\" \". |; ; _; ;;;"
486 print " /\"-/ | / / / / ;|; ;-\" | ;\';"
487 print ":- : \"\"\"----______ / / ____. . .\"\'. ;; .-\"..T\" ."
488 print "\'. \" ___ \"\": \'\"\"\"\"\"\"\"\"\"\"\"\"\"\" . ; ; ;; ;.\" .\" \'--\""
489 print " \", __ \"\"\" \"\"---... :- - - - - - - - - \' \' ; ; ; ;;\" .\""
490 print " /. ; \"\"\"---___ ; ; ; ;|.\"\""
491 print " : \": \"\"\"----. .-------. ; ; ; ;:"
492 print " \\ \'--__ \\ \\ \\ / | ; ;;"
493 print " \'-.. \"\"\"\"---___ : .______..\\ __/..-\"\"| ; ; ;"
494 print " \"\"--.. \"\"\"--\" m l s . \". . ;"
495 print " \"\"------... ..--\"\" \" :"
496 print " \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" \\ /"
499 print " Drive your simulation properly with SALOME!"
504 # Add the following two methods since logger is not pickable
505 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
506 def __getstate__(self):
507 d = dict(self.__dict__)
508 if hasattr(self, '_logger'):
512 def __setstate__(self, d):
513 self.__dict__.update(d) # I *think* this is a safe way to do it
515 # Excluding self._logger from pickle operation imply using the following method to access logger
517 if not hasattr(self, '_logger'):
518 self._logger = logging.getLogger(__name__)
519 #self._logger.setLevel(logging.DEBUG)
520 #self._logger.setLevel(logging.WARNING)
521 self._logger.setLevel(logging.ERROR)
525 if __name__ == "__main__":
526 if len(sys.argv) == 3:
527 context = pickle.loads(sys.argv[1])
528 args = pickle.loads(sys.argv[2])
530 (out, err) = context._startSalome(args)
532 sys.stdout.write(out)
534 sys.stderr.write(err)