1 # Copyright (C) 2013-2014 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=file1,...,filen]
42 start Launches SALOME virtual application [DEFAULT]
43 shell Executes a script under SALOME application environment
44 connect Connects a Python console to the active SALOME session
45 killall Kill all SALOME running sessions
46 info Display some information about SALOME
47 help Show this message
48 coffee Yes! SALOME can also make coffee!!"
50 Use salome start --help or salome shell --help
51 to show help on start and shell commands.
58 The SalomeContext class in an API to configure SALOME environment then
59 start SALOME using a single python command.
64 Initialize environment from a list of configuration files
65 identified by their names.
66 These files should be in appropriate (new .cfg) format.
67 However you can give old .sh environment files; in this case,
68 the SalomeContext class will try to automatically convert them
69 to .cfg format before setting the environment.
71 def __init__(self, configFileNames=0):
72 #it could be None explicitely (if user use multiples setVariable...for standalone)
73 if configFileNames is None:
75 configFileNames = configFileNames or []
76 if len(configFileNames) == 0:
77 raise SalomeContextException("No configuration files given")
79 reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']
80 for filename in configFileNames:
81 basename, extension = os.path.splitext(filename)
82 if extension == ".cfg":
83 self.__setEnvironmentFromConfigFile(filename, reserved)
84 elif extension == ".sh":
85 #new convert procedures, temporary could be use not to be automatically deleted
86 #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
87 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
89 convertEnvFileToConfigFile(filename, temp.name, reserved)
90 self.__setEnvironmentFromConfigFile(temp.name, reserved)
92 except (ConfigParser.ParsingError, ValueError) as e:
93 self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
97 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
100 def runSalome(self, args):
101 # Run this module as a script, in order to use appropriate Python interpreter
102 # according to current path (initialized from environment files).
105 # if "--shutdown-server" in e:
110 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
111 env_copy = os.environ.copy()
112 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)
113 msg = proc.communicate()
115 # self._killAll(args)
116 return msg, proc.returncode
119 """Append value to PATH environment variable"""
120 def addToPath(self, value):
121 self.addToVariable('PATH', value)
124 """Append value to LD_LIBRARY_PATH environment variable"""
125 def addToLdLibraryPath(self, value):
126 self.addToVariable('LD_LIBRARY_PATH', value)
129 """Append value to DYLD_LIBRARY_PATH environment variable"""
130 def addToDyldLibraryPath(self, value):
131 self.addToVariable('DYLD_LIBRARY_PATH', value)
134 """Append value to PYTHONPATH environment variable"""
135 def addToPythonPath(self, value):
136 self.addToVariable('PYTHONPATH', value)
139 """Set environment variable to value"""
140 def setVariable(self, name, value, overwrite=False):
141 env = os.getenv(name, '')
142 if env and not overwrite:
143 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
147 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
149 value = os.path.expandvars(value) # expand environment variables
150 self.getLogger().debug("Set environment variable: %s=%s", name, value)
151 os.environ[name] = value
154 """Unset environment variable"""
155 def unsetVariable(self, name):
156 if os.environ.has_key(name):
160 """Append value to environment variable"""
161 def addToVariable(self, name, value, separator=os.pathsep):
165 value = os.path.expandvars(value) # expand environment variables
166 self.getLogger().debug("Add to %s: %s", name, value)
167 env = os.getenv(name, None)
169 os.environ[name] = value
171 os.environ[name] = value + separator + env
174 ###################################
175 # This begins the private section #
176 ###################################
178 def __parseArguments(self, args):
179 if len(args) == 0 or args[0].startswith("-"):
185 availableCommands = {
186 'start' : '_runAppli',
187 'shell' : '_runSession',
188 'connect' : '_runConsole',
189 'killall': '_killAll',
192 'coffee' : '_makeCoffee'
195 if not command in availableCommands.keys():
199 return availableCommands[command], options
204 Args consist in a mandatory command followed by optionnal parameters.
205 See usage for details on commands.
207 def _startSalome(self, args):
210 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
212 path = os.path.join(absoluteAppliPath, "bin", "salome")
213 if not path in sys.path:
214 sys.path[:0] = [path]
218 command, options = self.__parseArguments(args)
222 if args and args[0] in ["-h","--help","help"]:
225 # try to default to "start" command
226 command = "_runAppli"
229 res = getattr(self, command)(options) # run appropriate method
230 return res or (None, None)
231 except SystemExit, returncode:
233 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
235 except StandardError:
236 self.getLogger().error("Unexpected error:")
238 traceback.print_exc()
240 except SalomeContextException, e:
241 self.getLogger().error(e)
245 def __setEnvironmentFromConfigFile(self, filename, reserved=None):
249 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
250 except SalomeContextException, e:
252 file_dir = os.path.dirname(filename)
253 file_base = os.path.basename(filename)
254 base_no_ext, ext = os.path.splitext(file_base)
255 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
256 if ext == ".cfg" and os.path.isfile(sh_file):
257 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
258 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
260 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
261 self.__setEnvironmentFromConfigFile(temp.name, reserved)
263 self.getLogger().warning(msg)
266 except (ConfigParser.ParsingError, ValueError) as e:
267 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
268 self.getLogger().error(msg)
272 self.getLogger().error(msg)
276 for var in unsetVars:
277 self.unsetVariable(var)
280 for reserved in reservedDict:
281 a = filter(None, reservedDict[reserved]) # remove empty elements
282 reformattedVals = os.pathsep.join(a)
283 self.addToVariable(reserved, reformattedVals)
286 for key,val in configVars:
287 self.setVariable(key, val, overwrite=True)
290 sys.path[:0] = os.getenv('PYTHONPATH','').split(os.pathsep)
293 def _runAppli(self, args=None):
296 # Initialize SALOME environment
297 sys.argv = ['runSalome'] + args
302 runSalome.runSalome()
305 def _runSession(self, args=None):
308 sys.argv = ['runSession'] + args
310 params, args = runSession.configureSession(args)
312 sys.argv = ['runSession'] + args
316 return runSession.runSession(params, args)
319 def _runConsole(self, args=None):
322 # Initialize SALOME environment
323 sys.argv = ['runConsole'] + args
327 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
328 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
329 return proc.communicate()
332 def _killAll(self, args=None):
336 import PortManager # mandatory
337 from multiprocessing import Process
338 from killSalomeWithPort import killMyPort
339 ports = PortManager.getBusyPorts()
344 with tempfile.NamedTemporaryFile():
345 p = Process(target = killMyPort, args=(port,))
349 from killSalome import killAllPorts
355 def _showInfo(self, args=None):
356 print "Running with python", platform.python_version()
357 self._runAppli(["--version"])
360 def _usage(self, unused=None):
364 def _makeCoffee(self, args=None):
367 print " ___...(-------)-....___"
368 print " .-\"\" ) ( \"\"-."
369 print " .-\'``\'|-._ ) _.-|"
370 print " / .--.| `\"\"---...........---\"\"` |"
378 print " _..---\"\"` \\ /`\"\"---.._"
379 print " .-\' \\ / \'-."
380 print " : `-.__ __.-\' :"
381 print " : ) \"\"---...---\"\" ( :"
382 print " \'._ `\"--...___...--\"` _.\'"
383 print " \\\"\"--..__ __..--\"\"/"
384 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
385 print " `\"\"--..,,_____ _____,,..--\"\"`"
386 print " `\"\"\"----\"\"\"`"
390 # Add the following two methods since logger is not pickable
391 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
392 def __getstate__(self):
393 d = dict(self.__dict__)
394 if hasattr(self, '_logger'):
398 def __setstate__(self, d):
399 self.__dict__.update(d) # I *think* this is a safe way to do it
401 # Excluding self._logger from pickle operation imply using the following method to access logger
403 if not hasattr(self, '_logger'):
404 self._logger = logging.getLogger(__name__)
405 #self._logger.setLevel(logging.DEBUG)
406 #self._logger.setLevel(logging.WARNING)
407 self._logger.setLevel(logging.ERROR)
411 if __name__ == "__main__":
412 if len(sys.argv) == 3:
413 context = pickle.loads(sys.argv[1])
414 args = pickle.loads(sys.argv[2])
416 (out, err) = context._startSalome(args)
418 sys.stdout.write(out)
420 sys.stderr.write(err)