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"
241 res = getattr(self, command)(options) # run appropriate method
242 return res or (None, None)
243 except SystemExit, returncode:
245 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
247 except StandardError:
248 self.getLogger().error("Unexpected error:")
250 traceback.print_exc()
252 except SalomeContextException, e:
253 self.getLogger().error(e)
257 def __setEnvironmentFromConfigFile(self, filename, reserved=None):
261 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
262 except SalomeContextException, e:
264 file_dir = os.path.dirname(filename)
265 file_base = os.path.basename(filename)
266 base_no_ext, ext = os.path.splitext(file_base)
267 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
268 if ext == ".cfg" and os.path.isfile(sh_file):
269 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
270 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
272 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
273 self.__setEnvironmentFromConfigFile(temp.name, reserved)
275 self.getLogger().warning(msg)
278 except (ConfigParser.ParsingError, ValueError) as e:
279 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
280 self.getLogger().error(msg)
284 self.getLogger().error(msg)
288 for var in unsetVars:
289 self.unsetVariable(var)
292 for reserved in reservedDict:
293 a = filter(None, reservedDict[reserved]) # remove empty elements
294 a = [ os.path.realpath(x) for x in a ]
295 reformattedVals = os.pathsep.join(a)
296 self.addToVariable(reserved, reformattedVals)
299 for key,val in configVars:
300 self.setVariable(key, val, overwrite=True)
303 pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
304 pythonpath = [ os.path.realpath(x) for x in pythonpath ]
305 sys.path[:0] = pythonpath
308 def _runAppli(self, args=None):
311 # Initialize SALOME environment
312 sys.argv = ['runSalome'] + args
317 runSalome.runSalome()
320 def _runSession(self, args=None):
323 sys.argv = ['runSession'] + args
325 params, args = runSession.configureSession(args, exe="salome shell")
327 sys.argv = ['runSession'] + args
331 return runSession.runSession(params, args)
334 def _runConsole(self, args=None):
337 # Initialize SALOME environment
338 sys.argv = ['runConsole'] + args
342 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
343 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
344 return proc.communicate()
347 def _killAll(self, args=None):
351 import PortManager # mandatory
352 from multiprocessing import Process
353 from killSalomeWithPort import killMyPort
354 ports = PortManager.getBusyPorts()
359 with tempfile.NamedTemporaryFile():
360 p = Process(target = killMyPort, args=(port,))
364 from killSalome import killAllPorts
370 def _showInfo(self, args=None):
371 print "Running with python", platform.python_version()
372 self._runAppli(["--version"])
375 def _usage(self, unused=None):
379 def _makeCoffee(self, args=None):
382 print " ___...(-------)-....___"
383 print " .-\"\" ) ( \"\"-."
384 print " .-\'``\'|-._ ) _.-|"
385 print " / .--.| `\"\"---...........---\"\"` |"
393 print " _..---\"\"` \\ /`\"\"---.._"
394 print " .-\' \\ / \'-."
395 print " : `-.__ __.-\' :"
396 print " : ) \"\"---...---\"\" ( :"
397 print " \'._ `\"--...___...--\"` _.\'"
398 print " \\\"\"--..__ __..--\"\"/"
399 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
400 print " `\"\"--..,,_____ _____,,..--\"\"`"
401 print " `\"\"\"----\"\"\"`"
405 # Add the following two methods since logger is not pickable
406 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
407 def __getstate__(self):
408 d = dict(self.__dict__)
409 if hasattr(self, '_logger'):
413 def __setstate__(self, d):
414 self.__dict__.update(d) # I *think* this is a safe way to do it
416 # Excluding self._logger from pickle operation imply using the following method to access logger
418 if not hasattr(self, '_logger'):
419 self._logger = logging.getLogger(__name__)
420 #self._logger.setLevel(logging.DEBUG)
421 #self._logger.setLevel(logging.WARNING)
422 self._logger.setLevel(logging.ERROR)
426 if __name__ == "__main__":
427 if len(sys.argv) == 3:
428 context = pickle.loads(sys.argv[1])
429 args = pickle.loads(sys.argv[2])
431 (out, err) = context._startSalome(args)
433 sys.stdout.write(out)
435 sys.stderr.write(err)