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
34 from salomeContextUtils import getScriptsAndArgs, formatScriptsAndArgs
37 #exeName = os.path.splitext(os.path.basename(__file__))[0]
40 Usage: salome [command] [options] [--config=file1,...,filen]
43 start Launches SALOME virtual application [DEFAULT]
44 shell Executes a script under SALOME application environment
45 connect Connects a Python console to the active SALOME session
46 killall Kill all SALOME running sessions
47 info Display some information about SALOME
48 help Show this message
49 coffee Yes! SALOME can also make coffee!!"
51 Use salome start --help or salome shell --help
52 to show help on start and shell commands.
59 The SalomeContext class in an API to configure SALOME environment then
60 start SALOME using a single python command.
65 Initialize environment from a list of configuration files
66 identified by their names.
67 These files should be in appropriate (new .cfg) format.
68 However you can give old .sh environment files; in this case,
69 the SalomeContext class will try to automatically convert them
70 to .cfg format before setting the environment.
72 def __init__(self, configFileNames=[]):
73 #it could be None explicitely (if user use multiples setVariable...for standalone)
74 if configFileNames==None:
77 if len(configFileNames) == 0:
78 raise SalomeContextException("No configuration files given")
80 reserved=['PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']
81 for filename in configFileNames:
82 basename, extension = os.path.splitext(filename)
83 if extension == ".cfg":
84 self.__setEnvironmentFromConfigFile(filename, reserved)
85 elif extension == ".sh":
86 #new convert procedures, temporary could be use not to be automatically deleted
87 #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
88 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
90 convertEnvFileToConfigFile(filename, temp.name, reserved)
91 self.__setEnvironmentFromConfigFile(temp.name, reserved)
93 except (ConfigParser.ParsingError, ValueError) as e:
94 self.getLogger().error("Invalid token found when parsing file: %s\n"%(filename))
98 self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
101 def runSalome(self, args):
102 # Run this module as a script, in order to use appropriate Python interpreter
103 # according to current path (initialized from environment files).
106 # if "--shutdown-server" in e:
110 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
111 proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeContext.py"), pickle.dumps(self), pickle.dumps(args)], shell=False, close_fds=True)
112 msg = proc.communicate()
114 # self._killAll(args)
115 return msg, proc.returncode
118 """Append value to PATH environment variable"""
119 def addToPath(self, value):
120 self.addToVariable('PATH', value)
123 """Append value to LD_LIBRARY_PATH environment variable"""
124 def addToLdLibraryPath(self, value):
125 self.addToVariable('LD_LIBRARY_PATH', value)
128 """Append value to DYLD_LIBRARY_PATH environment variable"""
129 def addToDyldLibraryPath(self, value):
130 self.addToVariable('DYLD_LIBRARY_PATH', value)
133 """Append value to PYTHONPATH environment variable"""
134 def addToPythonPath(self, value):
135 self.addToVariable('PYTHONPATH', value)
138 """Set environment variable to value"""
139 def setVariable(self, name, value, overwrite=False):
140 env = os.getenv(name, '')
141 if env and not overwrite:
142 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
146 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
148 value = os.path.expandvars(value) # expand environment variables
149 self.getLogger().debug("Set environment variable: %s=%s", name, value)
150 os.environ[name] = value
153 """Unset environment variable"""
154 def unsetVariable(self, name):
155 if os.environ.has_key(name):
159 """Append value to environment variable"""
160 def addToVariable(self, name, value, separator=os.pathsep):
164 value = os.path.expandvars(value) # expand environment variables
165 self.getLogger().debug("Add to %s: %s", name, value)
166 env = os.getenv(name, None)
168 os.environ[name] = value
170 os.environ[name] = value + separator + env
173 ###################################
174 # This begins the private section #
175 ###################################
177 def __parseArguments(self, args):
178 if len(args) == 0 or args[0].startswith("-"):
184 availableCommands = {
185 'start' : '_runAppli',
186 'shell' : '_runSession',
187 'connect' : '_runConsole',
188 'killall': '_killAll',
191 'coffee' : '_makeCoffee'
194 if not command in availableCommands.keys():
198 return availableCommands[command], options
203 Args consist in a mandatory command followed by optionnal parameters.
204 See usage for details on commands.
206 def _startSalome(self, args):
207 command, options = self.__parseArguments(args)
211 if args and args[0] in ["-h","--help","help"]:
214 # try to default to "start" command
215 command = "_runAppli"
218 res = getattr(self, command)(options) # run appropriate method
219 return res or (None, None)
220 except SystemExit, returncode:
222 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
224 except StandardError:
225 self.getLogger().error("Unexpected error:")
227 traceback.print_exc()
229 except SalomeContextException, e:
230 self.getLogger().error(e)
234 def __setEnvironmentFromConfigFile(self, filename, reserved=[]):
236 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
237 except SalomeContextException, e:
239 file_dir = os.path.dirname(filename)
240 file_base = os.path.basename(filename)
241 base_no_ext, ext = os.path.splitext(file_base)
242 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
243 if ext == ".cfg" and os.path.isfile(sh_file):
244 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
245 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
247 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
248 self.__setEnvironmentFromConfigFile(temp.name, reserved)
250 self.getLogger().warning(msg)
253 except (ConfigParser.ParsingError, ValueError) as e:
254 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
255 self.getLogger().error(msg)
259 self.getLogger().error(msg)
263 for var in unsetVars:
264 self.unsetVariable(var)
267 for reserved in reservedDict:
268 a = filter(None, reservedDict[reserved]) # remove empty elements
269 reformattedVals = ':'.join(a)
270 self.addToVariable(reserved, reformattedVals)
273 for key,val in configVars:
274 self.setVariable(key, val, overwrite=True)
277 sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
280 def _runAppli(self, args=[]):
281 # Initialize SALOME environment
282 sys.argv = ['runSalome'] + args
287 runSalome.runSalome()
290 def _runSession(self, args=[]):
291 sys.argv = ['runSession'] + args
293 runSession.configureSession(args)
298 scriptArgs = getScriptsAndArgs(args)
299 command = formatScriptsAndArgs(scriptArgs)
300 return runSession.runSession(command)
303 def _runConsole(self, args=[]):
304 # Initialize SALOME environment
305 sys.argv = ['runConsole'] + args
309 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
310 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
311 return proc.communicate()
314 def _killAll(self, args=[]):
316 import PortManager # mandatory
317 from multiprocessing import Process
318 from killSalomeWithPort import killMyPort
319 ports = PortManager.getBusyPorts()
324 with tempfile.NamedTemporaryFile():
325 p = Process(target = killMyPort, args=(port,))
329 from killSalome import killAllPorts
335 def _showInfo(self, args=[]):
336 print "Running with python", platform.python_version()
337 self._runAppli(["--version"])
340 def _usage(self, unused=[]):
344 def _makeCoffee(self, args=[]):
347 print " ___...(-------)-....___"
348 print " .-\"\" ) ( \"\"-."
349 print " .-\'``\'|-._ ) _.-|"
350 print " / .--.| `\"\"---...........---\"\"` |"
358 print " _..---\"\"` \\ /`\"\"---.._"
359 print " .-\' \\ / \'-."
360 print " : `-.__ __.-\' :"
361 print " : ) \"\"---...---\"\" ( :"
362 print " \'._ `\"--...___...--\"` _.\'"
363 print " \\\"\"--..__ __..--\"\"/"
364 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
365 print " `\"\"--..,,_____ _____,,..--\"\"`"
366 print " `\"\"\"----\"\"\"`"
370 # Add the following two methods since logger is not pickable
371 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
372 def __getstate__(self):
373 d = dict(self.__dict__)
374 if hasattr(self, '_logger'):
378 def __setstate__(self, d):
379 self.__dict__.update(d) # I *think* this is a safe way to do it
381 # Excluding self._logger from pickle operation imply using the following method to access logger
383 if not hasattr(self, '_logger'):
384 self._logger = logging.getLogger(__name__)
385 #self._logger.setLevel(logging.DEBUG)
386 #self._logger.setLevel(logging.WARNING)
387 self._logger.setLevel(logging.ERROR)
392 if __name__ == "__main__":
393 if len(sys.argv) == 3:
394 context = pickle.loads(sys.argv[1])
395 args = pickle.loads(sys.argv[2])
397 (out, err) = context._startSalome(args)
399 sys.stdout.write(out)
401 sys.stderr.write(err)