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).
115 mpi_module_option = "--with_mpi_module="
116 mpi_module = [x for x in args if x.startswith(mpi_module_option)]
118 mpi_module = mpi_module[0][len(mpi_module_option):]
119 print "Trying to load MPI module: %s..."%mpi_module,
121 out, err = subprocess.call(["modulecmd", "python", "load", mpi_module]).communicate()
122 exec out # define specific environment variables
125 print " ** Failed **"
127 args = [x for x in args if not x.startswith(mpi_module_option)]
131 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
132 env_copy = os.environ.copy()
133 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)
134 msg = proc.communicate()
135 return msg, proc.returncode
138 """Append value to PATH environment variable"""
139 def addToPath(self, value):
140 self.addToVariable('PATH', value)
143 """Append value to LD_LIBRARY_PATH environment variable"""
144 def addToLdLibraryPath(self, value):
145 self.addToVariable('LD_LIBRARY_PATH', value)
148 """Append value to DYLD_LIBRARY_PATH environment variable"""
149 def addToDyldLibraryPath(self, value):
150 self.addToVariable('DYLD_LIBRARY_PATH', value)
153 """Append value to PYTHONPATH environment variable"""
154 def addToPythonPath(self, value):
155 self.addToVariable('PYTHONPATH', value)
158 """Set environment variable to value"""
159 def setVariable(self, name, value, overwrite=False):
160 env = os.getenv(name, '')
161 if env and not overwrite:
162 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
166 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
168 value = os.path.expandvars(value) # expand environment variables
169 self.getLogger().debug("Set environment variable: %s=%s", name, value)
170 os.environ[name] = value
173 """Unset environment variable"""
174 def unsetVariable(self, name):
175 if os.environ.has_key(name):
179 """Append value to environment variable"""
180 def addToVariable(self, name, value, separator=os.pathsep):
184 value = os.path.expandvars(value) # expand environment variables
185 self.getLogger().debug("Add to %s: %s", name, value)
186 env = os.getenv(name, None)
188 os.environ[name] = value
190 os.environ[name] = value + separator + env
193 ###################################
194 # This begins the private section #
195 ###################################
197 def __parseArguments(self, args):
198 if len(args) == 0 or args[0].startswith("-"):
204 availableCommands = {
205 'start' : '_runAppli',
206 'shell' : '_runSession',
207 'connect' : '_runConsole',
208 'killall': '_killAll',
211 'coffee' : '_makeCoffee'
214 if not command in availableCommands.keys():
218 return availableCommands[command], options
223 Args consist in a mandatory command followed by optionnal parameters.
224 See usage for details on commands.
226 def _startSalome(self, args):
229 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
231 path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
232 if not path in sys.path:
233 sys.path[:0] = [path]
237 command, options = self.__parseArguments(args)
241 if args and args[0] in ["-h","--help","help"]:
244 # try to default to "start" command
245 command = "_runAppli"
248 res = getattr(self, command)(options) # run appropriate method
249 return res or (None, None)
250 except SystemExit, returncode:
252 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
254 except StandardError:
255 self.getLogger().error("Unexpected error:")
257 traceback.print_exc()
259 except SalomeContextException, e:
260 self.getLogger().error(e)
264 def __setEnvironmentFromConfigFile(self, filename, reserved=None):
268 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
269 except SalomeContextException, e:
271 file_dir = os.path.dirname(filename)
272 file_base = os.path.basename(filename)
273 base_no_ext, ext = os.path.splitext(file_base)
274 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
275 if ext == ".cfg" and os.path.isfile(sh_file):
276 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
277 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
279 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
280 self.__setEnvironmentFromConfigFile(temp.name, reserved)
282 self.getLogger().warning(msg)
285 except (ConfigParser.ParsingError, ValueError) as e:
286 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
287 self.getLogger().error(msg)
291 self.getLogger().error(msg)
295 for var in unsetVars:
296 self.unsetVariable(var)
299 for reserved in reservedDict:
300 a = filter(None, reservedDict[reserved]) # remove empty elements
301 a = [ os.path.realpath(x) for x in a ]
302 reformattedVals = os.pathsep.join(a)
303 self.addToVariable(reserved, reformattedVals)
306 for key,val in configVars:
307 self.setVariable(key, val, overwrite=True)
310 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
311 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
312 sys.path[:0] = pythonpath
315 def _runAppli(self, args=None):
318 # Initialize SALOME environment
319 sys.argv = ['runSalome'] + args
324 runSalome.runSalome()
327 def _runSession(self, args=None):
330 sys.argv = ['runSession'] + args
332 params, args = runSession.configureSession(args, exe="salome shell")
334 sys.argv = ['runSession'] + args
338 return runSession.runSession(params, args)
341 def _runConsole(self, args=None):
344 # Initialize SALOME environment
345 sys.argv = ['runConsole'] + args
349 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
350 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
351 return proc.communicate()
354 def _killAll(self, args=None):
358 import PortManager # mandatory
359 from multiprocessing import Process
360 from killSalomeWithPort import killMyPort
361 ports = PortManager.getBusyPorts()
366 with tempfile.NamedTemporaryFile():
367 p = Process(target = killMyPort, args=(port,))
371 from killSalome import killAllPorts
377 def _showInfo(self, args=None):
378 print "Running with python", platform.python_version()
379 self._runAppli(["--version"])
382 def _usage(self, unused=None):
386 def _makeCoffee(self, args=None):
389 print " ___...(-------)-....___"
390 print " .-\"\" ) ( \"\"-."
391 print " .-\'``\'|-._ ) _.-|"
392 print " / .--.| `\"\"---...........---\"\"` |"
400 print " _..---\"\"` \\ /`\"\"---.._"
401 print " .-\' \\ / \'-."
402 print " : `-.__ __.-\' :"
403 print " : ) \"\"---...---\"\" ( :"
404 print " \'._ `\"--...___...--\"` _.\'"
405 print " \\\"\"--..__ __..--\"\"/"
406 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
407 print " `\"\"--..,,_____ _____,,..--\"\"`"
408 print " `\"\"\"----\"\"\"`"
412 # Add the following two methods since logger is not pickable
413 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
414 def __getstate__(self):
415 d = dict(self.__dict__)
416 if hasattr(self, '_logger'):
420 def __setstate__(self, d):
421 self.__dict__.update(d) # I *think* this is a safe way to do it
423 # Excluding self._logger from pickle operation imply using the following method to access logger
425 if not hasattr(self, '_logger'):
426 self._logger = logging.getLogger(__name__)
427 #self._logger.setLevel(logging.DEBUG)
428 #self._logger.setLevel(logging.WARNING)
429 self._logger.setLevel(logging.ERROR)
433 if __name__ == "__main__":
434 if len(sys.argv) == 3:
435 context = pickle.loads(sys.argv[1])
436 args = pickle.loads(sys.argv[2])
438 (out, err) = context._startSalome(args)
440 sys.stdout.write(out)
442 sys.stderr.write(err)