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 shell Initializes SALOME environment, and executes scripts passed
46 connect Connects a Python console to the active SALOME session
47 killall Kill all SALOME running sessions for current user
48 info Display some information about SALOME
49 help Show this message
50 coffee Yes! SALOME can also make coffee!!
52 If no command is given, default to start.
56 Use salome <command> --help to show help on command ; available for start
59 --config=<file,folder,...>
60 ==========================
61 Initialize SALOME environment from a list of context files and/or a list
62 of folders containing context files. The list is comma-separated, whithout
70 The SalomeContext class in an API to configure SALOME environment then
71 start SALOME using a single python command.
76 Initialize environment from a list of configuration files
77 identified by their names.
78 These files should be in appropriate (new .cfg) format.
79 However you can give old .sh environment files; in this case,
80 the SalomeContext class will try to automatically convert them
81 to .cfg format before setting the environment.
83 def __init__(self, configFileNames=0):
84 #it could be None explicitely (if user use multiples setVariable...for standalone)
85 if configFileNames is None:
87 configFileNames = configFileNames or []
88 if len(configFileNames) == 0:
89 raise SalomeContextException("No configuration files given")
91 reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']
92 for filename in configFileNames:
93 basename, extension = os.path.splitext(filename)
94 if extension == ".cfg":
95 self.__setEnvironmentFromConfigFile(filename, reserved)
96 elif extension == ".sh":
97 #new convert procedures, temporary could be use not to be automatically deleted
98 #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
99 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
101 convertEnvFileToConfigFile(filename, temp.name, reserved)
102 self.__setEnvironmentFromConfigFile(temp.name, reserved)
104 except (ConfigParser.ParsingError, ValueError) as e:
105 self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
109 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
112 def runSalome(self, args):
113 # Run this module as a script, in order to use appropriate Python interpreter
114 # according to current path (initialized from environment files).
117 # if "--shutdown-server" in e:
122 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
123 env_copy = os.environ.copy()
124 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)
125 msg = proc.communicate()
127 # self._killAll(args)
128 return msg, proc.returncode
131 """Append value to PATH environment variable"""
132 def addToPath(self, value):
133 self.addToVariable('PATH', value)
136 """Append value to LD_LIBRARY_PATH environment variable"""
137 def addToLdLibraryPath(self, value):
138 self.addToVariable('LD_LIBRARY_PATH', value)
141 """Append value to DYLD_LIBRARY_PATH environment variable"""
142 def addToDyldLibraryPath(self, value):
143 self.addToVariable('DYLD_LIBRARY_PATH', value)
146 """Append value to PYTHONPATH environment variable"""
147 def addToPythonPath(self, value):
148 self.addToVariable('PYTHONPATH', value)
151 """Set environment variable to value"""
152 def setVariable(self, name, value, overwrite=False):
153 env = os.getenv(name, '')
154 if env and not overwrite:
155 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
159 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
161 value = os.path.expandvars(value) # expand environment variables
162 self.getLogger().debug("Set environment variable: %s=%s", name, value)
163 os.environ[name] = value
166 """Unset environment variable"""
167 def unsetVariable(self, name):
168 if os.environ.has_key(name):
172 """Append value to environment variable"""
173 def addToVariable(self, name, value, separator=os.pathsep):
177 value = os.path.expandvars(value) # expand environment variables
178 self.getLogger().debug("Add to %s: %s", name, value)
179 env = os.getenv(name, None)
181 os.environ[name] = value
183 os.environ[name] = value + separator + env
186 ###################################
187 # This begins the private section #
188 ###################################
190 def __parseArguments(self, args):
191 if len(args) == 0 or args[0].startswith("-"):
197 availableCommands = {
198 'start' : '_runAppli',
199 'shell' : '_runSession',
200 'connect' : '_runConsole',
201 'killall': '_killAll',
204 'coffee' : '_makeCoffee'
207 if not command in availableCommands.keys():
211 return availableCommands[command], options
216 Args consist in a mandatory command followed by optionnal parameters.
217 See usage for details on commands.
219 def _startSalome(self, args):
222 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
224 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
225 if not path in sys.path:
226 sys.path[:0] = [path]
230 command, options = self.__parseArguments(args)
234 if args and args[0] in ["-h","--help","help"]:
237 # try to default to "start" command
238 command = "_runAppli"
240 mpi_module_option = "--with_mpi_module="
241 mpi_module = [x for x in args if x.startswith(mpi_module_option)]
243 mpi_module = mpi_module[0][len(mpi_module_option):]
244 print "Trying to load MPI module: %s..."%mpi_module,
247 subprocess.call(["module", "load", mpi_module])
250 print " ** Failed **"
252 options = [x for x in options if not x.startswith(mpi_module_option)]
256 res = getattr(self, command)(options) # run appropriate method
257 return res or (None, None)
258 except SystemExit, returncode:
260 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
262 except StandardError:
263 self.getLogger().error("Unexpected error:")
265 traceback.print_exc()
267 except SalomeContextException, e:
268 self.getLogger().error(e)
272 def __setEnvironmentFromConfigFile(self, filename, reserved=None):
276 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
277 except SalomeContextException, e:
279 file_dir = os.path.dirname(filename)
280 file_base = os.path.basename(filename)
281 base_no_ext, ext = os.path.splitext(file_base)
282 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
283 if ext == ".cfg" and os.path.isfile(sh_file):
284 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
285 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
287 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
288 self.__setEnvironmentFromConfigFile(temp.name, reserved)
290 self.getLogger().warning(msg)
293 except (ConfigParser.ParsingError, ValueError) as e:
294 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
295 self.getLogger().error(msg)
299 self.getLogger().error(msg)
303 for var in unsetVars:
304 self.unsetVariable(var)
307 for reserved in reservedDict:
308 a = filter(None, reservedDict[reserved]) # remove empty elements
309 a = [ os.path.realpath(x) for x in a ]
310 reformattedVals = os.pathsep.join(a)
311 self.addToVariable(reserved, reformattedVals)
314 for key,val in configVars:
315 self.setVariable(key, val, overwrite=True)
318 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
319 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
320 sys.path[:0] = pythonpath
323 def _runAppli(self, args=None):
326 # Initialize SALOME environment
327 sys.argv = ['runSalome'] + args
332 runSalome.runSalome()
335 def _runSession(self, args=None):
338 sys.argv = ['runSession'] + args
340 params, args = runSession.configureSession(args, exe="salome shell")
342 sys.argv = ['runSession'] + args
346 return runSession.runSession(params, args)
349 def _runConsole(self, args=None):
352 # Initialize SALOME environment
353 sys.argv = ['runConsole'] + args
357 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
358 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
359 return proc.communicate()
362 def _killAll(self, args=None):
366 import PortManager # mandatory
367 from multiprocessing import Process
368 from killSalomeWithPort import killMyPort
369 ports = PortManager.getBusyPorts()
374 with tempfile.NamedTemporaryFile():
375 p = Process(target = killMyPort, args=(port,))
379 from killSalome import killAllPorts
385 def _showInfo(self, args=None):
386 print "Running with python", platform.python_version()
387 self._runAppli(["--version"])
390 def _usage(self, unused=None):
394 def _makeCoffee(self, args=None):
397 print " ___...(-------)-....___"
398 print " .-\"\" ) ( \"\"-."
399 print " .-\'``\'|-._ ) _.-|"
400 print " / .--.| `\"\"---...........---\"\"` |"
408 print " _..---\"\"` \\ /`\"\"---.._"
409 print " .-\' \\ / \'-."
410 print " : `-.__ __.-\' :"
411 print " : ) \"\"---...---\"\" ( :"
412 print " \'._ `\"--...___...--\"` _.\'"
413 print " \\\"\"--..__ __..--\"\"/"
414 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
415 print " `\"\"--..,,_____ _____,,..--\"\"`"
416 print " `\"\"\"----\"\"\"`"
420 # Add the following two methods since logger is not pickable
421 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
422 def __getstate__(self):
423 d = dict(self.__dict__)
424 if hasattr(self, '_logger'):
428 def __setstate__(self, d):
429 self.__dict__.update(d) # I *think* this is a safe way to do it
431 # Excluding self._logger from pickle operation imply using the following method to access logger
433 if not hasattr(self, '_logger'):
434 self._logger = logging.getLogger(__name__)
435 #self._logger.setLevel(logging.DEBUG)
436 #self._logger.setLevel(logging.WARNING)
437 self._logger.setLevel(logging.ERROR)
441 if __name__ == "__main__":
442 if len(sys.argv) == 3:
443 context = pickle.loads(sys.argv[1])
444 args = pickle.loads(sys.argv[2])
446 (out, err) = context._startSalome(args)
448 sys.stdout.write(out)
450 sys.stderr.write(err)