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:
109 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
110 proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeContext.py"), pickle.dumps(self), pickle.dumps(args)], shell=False, close_fds=True)
111 msg = proc.communicate()
113 # self._killAll(args)
114 return msg, proc.returncode
117 """Append value to PATH environment variable"""
118 def addToPath(self, value):
119 self.addToVariable('PATH', value)
122 """Append value to LD_LIBRARY_PATH environment variable"""
123 def addToLdLibraryPath(self, value):
124 self.addToVariable('LD_LIBRARY_PATH', value)
127 """Append value to DYLD_LIBRARY_PATH environment variable"""
128 def addToDyldLibraryPath(self, value):
129 self.addToVariable('DYLD_LIBRARY_PATH', value)
132 """Append value to PYTHONPATH environment variable"""
133 def addToPythonPath(self, value):
134 self.addToVariable('PYTHONPATH', value)
137 """Set environment variable to value"""
138 def setVariable(self, name, value, overwrite=False):
139 env = os.getenv(name, '')
140 if env and not overwrite:
141 self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
145 self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
147 value = os.path.expandvars(value) # expand environment variables
148 self.getLogger().debug("Set environment variable: %s=%s", name, value)
149 os.environ[name] = value
152 """Unset environment variable"""
153 def unsetVariable(self, name):
154 if os.environ.has_key(name):
158 """Append value to environment variable"""
159 def addToVariable(self, name, value, separator=os.pathsep):
163 value = os.path.expandvars(value) # expand environment variables
164 self.getLogger().debug("Add to %s: %s", name, value)
165 env = os.getenv(name, None)
167 os.environ[name] = value
169 os.environ[name] = value + separator + env
172 ###################################
173 # This begins the private section #
174 ###################################
176 def __parseArguments(self, args):
177 if len(args) == 0 or args[0].startswith("-"):
183 availableCommands = {
184 'start' : '_runAppli',
185 'shell' : '_runSession',
186 'connect' : '_runConsole',
187 'killall': '_killAll',
190 'coffee' : '_makeCoffee'
193 if not command in availableCommands.keys():
197 return availableCommands[command], options
202 Args consist in a mandatory command followed by optionnal parameters.
203 See usage for details on commands.
205 def _startSalome(self, args):
206 command, options = self.__parseArguments(args)
210 if args and args[0] in ["-h","--help","help"]:
213 # try to default to "start" command
214 command = "_runAppli"
217 res = getattr(self, command)(options) # run appropriate method
218 return res or (None, None)
219 except SystemExit, returncode:
221 self.getLogger().warning("SystemExit %s in method %s.", returncode, command)
223 except StandardError:
224 self.getLogger().error("Unexpected error:")
226 traceback.print_exc()
228 except SalomeContextException, e:
229 self.getLogger().error(e)
233 def __setEnvironmentFromConfigFile(self, filename, reserved=None):
237 unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
238 except SalomeContextException, e:
240 file_dir = os.path.dirname(filename)
241 file_base = os.path.basename(filename)
242 base_no_ext, ext = os.path.splitext(file_base)
243 sh_file = os.path.join(file_dir, base_no_ext+'.sh')
244 if ext == ".cfg" and os.path.isfile(sh_file):
245 msg += "Found similar %s file; trying to parse this one instead..."%(base_no_ext+'.sh')
246 temp = tempfile.NamedTemporaryFile(suffix='.cfg')
248 convertEnvFileToConfigFile(sh_file, temp.name, reserved)
249 self.__setEnvironmentFromConfigFile(temp.name, reserved)
251 self.getLogger().warning(msg)
254 except (ConfigParser.ParsingError, ValueError) as e:
255 msg += "Invalid token found when parsing file: %s\n"%(sh_file)
256 self.getLogger().error(msg)
260 self.getLogger().error(msg)
264 for var in unsetVars:
265 self.unsetVariable(var)
268 for reserved in reservedDict:
269 a = filter(None, reservedDict[reserved]) # remove empty elements
270 reformattedVals = os.pathsep.join(a)
271 self.addToVariable(reserved, reformattedVals)
274 for key,val in configVars:
275 self.setVariable(key, val, overwrite=True)
278 sys.path[:0] = os.getenv('PYTHONPATH','').split(os.pathsep)
281 def _runAppli(self, args=None):
284 # Initialize SALOME environment
285 sys.argv = ['runSalome'] + args
290 runSalome.runSalome()
293 def _runSession(self, args=None):
296 sys.argv = ['runSession'] + args
298 params, args = runSession.configureSession(args)
300 sys.argv = ['runSession'] + args
304 return runSession.runSession(params, args)
307 def _runConsole(self, args=None):
310 # Initialize SALOME environment
311 sys.argv = ['runConsole'] + args
315 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
316 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
317 return proc.communicate()
320 def _killAll(self, args=None):
324 import PortManager # mandatory
325 from multiprocessing import Process
326 from killSalomeWithPort import killMyPort
327 ports = PortManager.getBusyPorts()
332 with tempfile.NamedTemporaryFile():
333 p = Process(target = killMyPort, args=(port,))
337 from killSalome import killAllPorts
343 def _showInfo(self, args=None):
344 print "Running with python", platform.python_version()
345 self._runAppli(["--version"])
348 def _usage(self, unused=None):
352 def _makeCoffee(self, args=None):
355 print " ___...(-------)-....___"
356 print " .-\"\" ) ( \"\"-."
357 print " .-\'``\'|-._ ) _.-|"
358 print " / .--.| `\"\"---...........---\"\"` |"
366 print " _..---\"\"` \\ /`\"\"---.._"
367 print " .-\' \\ / \'-."
368 print " : `-.__ __.-\' :"
369 print " : ) \"\"---...---\"\" ( :"
370 print " \'._ `\"--...___...--\"` _.\'"
371 print " \\\"\"--..__ __..--\"\"/"
372 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
373 print " `\"\"--..,,_____ _____,,..--\"\"`"
374 print " `\"\"\"----\"\"\"`"
378 # Add the following two methods since logger is not pickable
379 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
380 def __getstate__(self):
381 d = dict(self.__dict__)
382 if hasattr(self, '_logger'):
386 def __setstate__(self, d):
387 self.__dict__.update(d) # I *think* this is a safe way to do it
389 # Excluding self._logger from pickle operation imply using the following method to access logger
391 if not hasattr(self, '_logger'):
392 self._logger = logging.getLogger(__name__)
393 #self._logger.setLevel(logging.DEBUG)
394 #self._logger.setLevel(logging.WARNING)
395 self._logger.setLevel(logging.ERROR)
399 if __name__ == "__main__":
400 if len(sys.argv) == 3:
401 context = pickle.loads(sys.argv[1])
402 args = pickle.loads(sys.argv[2])
404 (out, err) = context._startSalome(args)
406 sys.stdout.write(out)
408 sys.stderr.write(err)