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=<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.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 reformattedVals = os.pathsep.join(a)
295 self.addToVariable(reserved, reformattedVals)
298 for key,val in configVars:
299 self.setVariable(key, val, overwrite=True)
302 sys.path[:0] = os.getenv('PYTHONPATH','').split(os.pathsep)
305 def _runAppli(self, args=None):
308 # Initialize SALOME environment
309 sys.argv = ['runSalome'] + args
314 runSalome.runSalome()
317 def _runSession(self, args=None):
320 sys.argv = ['runSession'] + args
322 params, args = runSession.configureSession(args)
324 sys.argv = ['runSession'] + args
328 return runSession.runSession(params, args)
331 def _runConsole(self, args=None):
334 # Initialize SALOME environment
335 sys.argv = ['runConsole'] + args
339 cmd = ["python", "-c", "import runConsole\nrunConsole.connect()" ]
340 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
341 return proc.communicate()
344 def _killAll(self, args=None):
348 import PortManager # mandatory
349 from multiprocessing import Process
350 from killSalomeWithPort import killMyPort
351 ports = PortManager.getBusyPorts()
356 with tempfile.NamedTemporaryFile():
357 p = Process(target = killMyPort, args=(port,))
361 from killSalome import killAllPorts
367 def _showInfo(self, args=None):
368 print "Running with python", platform.python_version()
369 self._runAppli(["--version"])
372 def _usage(self, unused=None):
376 def _makeCoffee(self, args=None):
379 print " ___...(-------)-....___"
380 print " .-\"\" ) ( \"\"-."
381 print " .-\'``\'|-._ ) _.-|"
382 print " / .--.| `\"\"---...........---\"\"` |"
390 print " _..---\"\"` \\ /`\"\"---.._"
391 print " .-\' \\ / \'-."
392 print " : `-.__ __.-\' :"
393 print " : ) \"\"---...---\"\" ( :"
394 print " \'._ `\"--...___...--\"` _.\'"
395 print " \\\"\"--..__ __..--\"\"/"
396 print " \'._ \"\"\"----.....______.....----\"\"\" _.\'"
397 print " `\"\"--..,,_____ _____,,..--\"\"`"
398 print " `\"\"\"----\"\"\"`"
402 # Add the following two methods since logger is not pickable
403 # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
404 def __getstate__(self):
405 d = dict(self.__dict__)
406 if hasattr(self, '_logger'):
410 def __setstate__(self, d):
411 self.__dict__.update(d) # I *think* this is a safe way to do it
413 # Excluding self._logger from pickle operation imply using the following method to access logger
415 if not hasattr(self, '_logger'):
416 self._logger = logging.getLogger(__name__)
417 #self._logger.setLevel(logging.DEBUG)
418 #self._logger.setLevel(logging.WARNING)
419 self._logger.setLevel(logging.ERROR)
423 if __name__ == "__main__":
424 if len(sys.argv) == 3:
425 context = pickle.loads(sys.argv[1])
426 args = pickle.loads(sys.argv[2])
428 (out, err) = context._startSalome(args)
430 sys.stdout.write(out)
432 sys.stderr.write(err)