Salome HOME
[bos #29467] [EDF] (2022-T1) Logging of SALOME usage: specific log in SALOME
[modules/kernel.git] / bin / runSalomeCommon.py
1 #!/usr/bin/env python3
2 #  -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2007-2023  CEA, EDF, OPEN CASCADE
4 #
5 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
6 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
7 #
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 #
22 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #
24
25 ## @package runSalome
26 # \brief Module that provides services to launch SALOME
27 #
28
29 import sys, os, string, glob, time, pickle, re
30 import setenv
31 from server import process_id, Server
32 import json
33 import subprocess
34 from salomeContextUtils import ScriptAndArgsObjectEncoder
35 import platform
36 import logging
37
38 ## Setting formatter in setVerbose() was commented because adding of handler
39 ## breaks using of root logger in other modules and cause many double lines in logs.
40 #FORMAT = '%(levelname)s : %(asctime)s : [%(filename)s:%(funcName)s:%(lineno)s] : %(message)s'
41 #logging.basicConfig(format=FORMAT)
42 logger = logging.getLogger()
43
44 class ColoredFormatter(logging.Formatter):
45     BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(30,38)
46     COLORS = { 'WARNING': YELLOW, 'INFO': WHITE, 'DEBUG': BLUE, 'CRITICAL': YELLOW, 'ERROR': RED }
47     def __init__(self, *args, **kwargs):
48         logging.Formatter.__init__(self, *args, **kwargs)
49     def format(self, record):
50         RESET_SEQ = "\033[0m"
51         COLOR_SEQ = "\033[1;%dm"
52         import inspect
53         frame = inspect.currentframe()
54         for i in range(8):
55             frame = frame.f_back
56         record.levelname = COLOR_SEQ % ColoredFormatter.COLORS[record.levelname] + record.levelname + RESET_SEQ
57         record.msg = "{} ( callsite is {} of file \"{}\" at line {} )".format(record.msg, frame.f_code.co_name,inspect.getsourcefile(frame),inspect.getlineno(frame) )
58         return logging.Formatter.format(self, record)
59
60 class BackTraceFormatter(logging.Formatter):
61     def __init__(self, *args, **kwargs):
62         logging.Formatter.__init__(self, *args, **kwargs)
63     def format(self, record):
64         import inspect
65         frame = inspect.currentframe()
66         # go upward of the stack to catch the effective callsite. Not very steady....
67         # should be replaced by an analysis of frame.f_code
68         for i in range(8):
69             frame = frame.f_back
70         record.msg = "{} ( callsite is {} of file \"{}\" at line {} )".format(record.msg, frame.f_code.co_name,inspect.getsourcefile(frame),inspect.getlineno(frame) )
71         return logging.Formatter.format(self, record)
72
73 def setVerbose(verbose):
74     from packaging import version
75     current_version = version.parse("{}.{}".format(sys.version_info.major,sys.version_info.minor))
76     version_ref = version.parse("3.5.0")
77     global logger
78     formatter = None
79     if current_version >= version_ref:
80         formatter = BackTraceFormatter('%(levelname)s : %(asctime)s : %(message)s ',style='%')
81     else:
82         formatter = logging.Formatter('%(levelname)s : %(asctime)s : %(message)s ',style='%')
83     formatter.default_time_format = '%H:%M:%S'
84     formatter.default_msec_format = "%s.%03d"
85     stream_handler = logging.StreamHandler()
86     stream_handler.setFormatter(formatter)
87     logger.addHandler(stream_handler)
88
89     verbose_map = { "0": logging.WARNING, "1": logging.INFO, "2": logging.DEBUG}
90     if verbose in verbose_map:
91         logger.setLevel(verbose_map[verbose])
92
93 # -----------------------------------------------------------------------------
94 #
95 # Class definitions to launch CORBA Servers
96 #
97
98 class InterpServer(Server):
99     def __init__(self,args):
100         self.args=args
101         if sys.platform == "win32":
102           self.CMD=['cmd', '/c', 'start cmd.exe', '/K', 'python']
103         elif sys.platform == "darwin":
104           env_ld_library_path=['env', 'DYLD_LIBRARY_PATH=' + os.getenv("LD_LIBRARY_PATH")]
105           self.CMD=['xterm', '-e'] + env_ld_library_path + ['python']
106         else:
107           env_ld_library_path=['env', 'LD_LIBRARY_PATH=' + os.getenv("LD_LIBRARY_PATH")]
108           self.CMD=['xterm', '-e'] + env_ld_library_path + ['python']
109
110     def run(self):
111         global process_id
112         command = self.CMD
113         print("INTERPSERVER::command = ", command)
114         import subprocess
115         pid = subprocess.Popen(command).pid
116         process_id[pid]=self.CMD
117         self.PID = pid
118
119 # ---
120
121 def get_cata_path(list_modules,modules_root_dir):
122     """Build a list of catalog paths (cata_path) to initialize the ModuleCatalog server
123     """
124     modules_cata={}
125     cata_path=[]
126
127     for module in list_modules:
128         if module in modules_root_dir:
129             module_root_dir=modules_root_dir[module]
130             module_cata=module+"Catalog.xml"
131             cata_file=os.path.join(module_root_dir, "share",setenv.salome_subdir, "resources",module.lower(), module_cata)
132
133             if os.path.exists(cata_file):
134                 cata_path.append(cata_file)
135                 modules_cata[module]=cata_file
136             else:
137                 cata_file=os.path.join(module_root_dir, "share",setenv.salome_subdir, "resources", module_cata)
138                 if os.path.exists(cata_file):
139                     cata_path.append(cata_file)
140                     modules_cata[module]=cata_file
141
142     for path in os.getenv("SALOME_CATALOGS_PATH","").split(os.pathsep):
143         if os.path.exists(path):
144             for cata_file in glob.glob(os.path.join(path,"*Catalog.xml")):
145                 module_name= os.path.basename(cata_file)[:-11]
146                 if module_name not in modules_cata:
147                     cata_path.append(cata_file)
148                     modules_cata[module_name]=cata_file
149
150     return cata_path
151
152 class CatalogServer(Server):
153     def __init__(self,args):
154         self.args=args
155         self.initArgs()
156         self.SCMD1=['SALOME_ModuleCatalog_Server']
157         if 'launcher' in self.args:
158             pos = args['launcher'].find(":")
159             if pos != -1:
160               self.SCMD1+=['-ORBInitRef']
161               machine = args['launcher'][0:pos]
162               port = args['launcher'][pos+1:]
163               self.SCMD1+=["NameService=corbaname::" + machine + ":" + port]
164         self.SCMD1+=['-common']
165         self.SCMD2=[]
166         home_dir=os.path.expanduser("~")
167         if home_dir is not None:
168             self.SCMD2=['-personal',os.path.join(home_dir,'Salome', 'resources', 'CatalogModulePersonnel.xml')]
169
170     def setpath(self,modules_list,modules_root_dir):
171         list_modules = modules_list[:]
172         list_modules.reverse()
173         if self.args["gui"] :
174             list_modules = ["KERNEL", "GUI"] + list_modules
175         else :
176             list_modules = ["KERNEL"] + list_modules
177
178         cata_path=get_cata_path(list_modules,modules_root_dir)
179
180         self.CMD=self.SCMD1 + ['"' + '"::"'.join(cata_path) + '"'] + self.SCMD2
181
182 # ---
183
184 class SalomeDSServer(Server):
185     def __init__(self,args):
186         self.args=args
187         self.initArgs()
188         self.CMD=['SALOMEDS_Server']
189         if 'launcher' in self.args:
190             pos = args['launcher'].find(":")
191             if pos != -1:
192               self.CMD+=['-ORBInitRef']
193               machine = args['launcher'][0:pos]
194               port = args['launcher'][pos+1:]
195               self.CMD+=["NameService=corbaname::" + machine + ":" + port]
196
197 # ---
198
199 class ConnectionManagerServer(Server):
200     def __init__(self,args):
201         self.args=args
202         self.initArgs()
203         self.CMD=['SALOME_ConnectionManagerServer']
204         if 'launcher' in self.args:
205             pos = args['launcher'].find(":")
206             if pos != -1:
207               self.CMD+=['-ORBInitRef']
208               machine = args['launcher'][0:pos]
209               port = args['launcher'][pos+1:]
210               self.CMD+=["NameService=corbaname::" + machine + ":" + port]
211
212
213 # ---
214
215 class RegistryServer(Server):
216     def __init__(self,args):
217         self.args=args
218         self.initArgs()
219         self.CMD=['SALOME_Registry_Server', '--salome_session','theSession']
220         if 'launcher' in self.args:
221             pos = args['launcher'].find(":")
222             if pos != -1:
223               self.CMD+=['-ORBInitRef']
224               machine = args['launcher'][0:pos]
225               port = args['launcher'][pos+1:]
226               self.CMD+=["NameService=corbaname::" + machine + ":" + port]
227
228 # ---
229
230 class ContainerCPPServer(Server):
231     def __init__(self,args,with_gui=False):
232         self.args=args
233         self.initArgs()
234         self.CMD=['SALOME_Container']
235         if 'launcher' in self.args:
236             pos = args['launcher'].find(":")
237             if pos != -1:
238               self.CMD+=['-ORBInitRef']
239               machine = args['launcher'][0:pos]
240               port = args['launcher'][pos+1:]
241               self.CMD+=["NameService=corbaname::" + machine + ":" + port]
242         self.CMD+=['FactoryServer']
243         if not with_gui and self.args["valgrind_session"]:
244             l = ["valgrind"]
245             val = os.getenv("VALGRIND_OPTIONS")
246             if val:
247                 l += val.split()
248                 pass
249             self.CMD = l + self.CMD
250             pass
251
252 # ---
253
254 class LoggerServer(Server):
255     def __init__(self,args):
256         self.args=args
257         self.initArgs()
258         from salome_utils import generateFileName, getLogDir
259         logfile = generateFileName( getLogDir(),
260                                     prefix="logger",
261                                     extension="log",
262                                     with_username=True,
263                                     with_hostname=True,
264                                     with_port=True)
265         print("===========================================================")
266         print("Logger server: put log to the file:")
267         print(logfile)
268         print("===========================================================")
269         self.CMD=['SALOME_Logger_Server', logfile]
270         pass
271     pass # end of LoggerServer class
272
273 # ---
274 import abc
275 import tempfile
276 class CommonSessionServer(Server):
277     def __init__(self,args,modules_list,modules_root_dir):
278         self.args = args.copy()
279         # Bug 11512 (Problems with runSalome --xterm on Mandrake and Debian Sarge)
280         #self.args['xterm']=0
281         #
282         self.initArgs()
283         self.SCMD1=[self.getSessionServerExe()]
284         if "SQUISH_PREFIX" in os.environ:
285             if platform.system() == "Windows" :
286                 self.SCMD1 = [os.path.join(os.getenv("SQUISH_PREFIX"), "bin", "dllpreload.exe"),os.path.join(os.getenv("SQUISH_SALOME_PATH"), "W64", "GUI", "bin", "salome", self.SCMD1[0])]
287             else :
288                 os.environ["LD_LIBRARY_PATH"] = os.environ["SQUISH_PREFIX"] + "/lib:" + os.environ["LD_LIBRARY_PATH"]
289         self.SCMD2=[]
290         if 'launcher' in self.args:
291             pos = args['launcher'].find(":")
292             if pos != -1:
293               self.SCMD1+=['-ORBInitRef']
294               machine = args['launcher'][0:pos]
295               port = args['launcher'][pos+1:]
296               self.SCMD1+=["NameService=corbaname::" + machine + ":" + port]
297         if 'registry' in self.args['embedded']:
298             self.SCMD1+=['--with','Registry',
299                          '(','--salome_session','theSession',')']
300         if 'moduleCatalog' in self.args['embedded']:
301             self.SCMD1+=['--with','ModuleCatalog','(','-common']
302             home_dir=os.path.expanduser("~")
303             if home_dir is not None:
304                 self.SCMD2+=['-personal',os.path.join(home_dir,'Salome','resources','CatalogModulePersonnel.xml')]
305             self.SCMD2+=[')']
306         if 'study' in self.args['embedded']:
307             self.SCMD2+=['--with','SALOMEDS','(',')']
308         if 'cppContainer' in self.args['embedded']:
309             self.SCMD2+=['--with','Container','(','FactoryServer',')']
310         if 'SalomeAppEngine' in self.args['embedded']:
311             self.SCMD2+=['--with','SalomeAppEngine','(',')']
312
313         if 'cppContainer' in self.args['standalone'] or 'cppContainer' in self.args['embedded']:
314             self.SCMD2+=['CPP']
315         if 'pyContainer' in self.args['standalone'] or 'pyContainer' in self.args['embedded']:
316             raise Exception('Python containers no longer supported')
317         if self.args['gui']:
318             session_gui = self.args.get('session_gui', True)
319             if not session_gui:
320                 self.SCMD2+=['--hide-desktop']
321             else:
322                 if not self.args['splash']:
323                     self.SCMD2+=['--hide-splash']
324                     pass
325                 if self.args['study_hdf'] is not None:
326                     self.SCMD2+=['--study-hdf=%s'%self.args['study_hdf']]
327                     pass
328                 pass
329                 if 'pyscript' in self.args and len(self.args['pyscript']) > 0:
330                     msg = json.dumps(self.args['pyscript'], cls=ScriptAndArgsObjectEncoder)
331                     self.SCMD2+=['--pyscript=%s'%(msg)]
332                     pass
333                 pass
334                 if self.args['gui_log_file'] is not None:
335                     self.SCMD2+=['--gui-log-file=%s'%self.args['gui_log_file']]
336                     pass
337                 pass
338             pass
339         if self.args['noexcepthandler']:
340             self.SCMD2+=['--no-exception-handler']
341         if 'user_config' in self.args:
342             self.SCMD2+=['--resources=%s'%self.args['user_config']]
343         if 'modules' in self.args:
344             list_modules = []
345             #keep only modules with GUI
346             for m in modules_list:
347               if m not in modules_root_dir:
348                 list_modules.insert(0,m)
349               else:
350                 fr1 = os.path.join(modules_root_dir[m],"share","salome","resources",m.lower(),"SalomeApp.xml")
351                 fr2 = os.path.join(modules_root_dir[m],"share","salome","resources","SalomeApp.xml")
352                 if os.path.exists(fr1) or os.path.exists(fr2):
353                   list_modules.insert(0,m)
354             list_modules.reverse()
355             self.SCMD2+=['--modules (%s)' % ":".join(list_modules)]
356             pass
357         if 'language' in self.args:
358             self.SCMD2+=['--language=%s' % self.args['language']]
359         os_handle, iorfakens = tempfile.mkstemp()
360         self.iorfakens = iorfakens
361         os.close(os_handle)
362         self.SCMD2+=["--iorfakens={}".format(iorfakens)]
363         pass
364
365     @abc.abstractmethod
366     def getSessionServerExe(self):
367         pass
368     
369     def setpath(self,modules_list,modules_root_dir):
370         list_modules = modules_list[:]
371         list_modules.reverse()
372         if self.args["gui"] :
373             list_modules = ["KERNEL", "GUI"] + list_modules
374         else :
375             list_modules = ["KERNEL"] + list_modules
376
377         cata_path=get_cata_path(list_modules,modules_root_dir)
378
379         if ("gui" in self.args) & ('moduleCatalog' in self.args['embedded']):
380             #Use '::' instead ":" because drive path with "D:\" is invalid on windows platform
381             self.CMD=self.SCMD1 + ['"' + '"::"'.join(cata_path) + '"'] + self.SCMD2
382         else:
383             self.CMD=self.SCMD1 + self.SCMD2
384         if 'test' in self.args:
385             self.CMD+=['-test'] + self.args['test']
386         elif 'play' in self.args:
387             self.CMD+=['-play'] + self.args['play']
388
389         if self.args["gdb_session"] or self.args["ddd_session"]:
390             f = open(".gdbinit4salome", "w")
391             f.write("set args ")
392             args = " ".join(self.CMD[1:])
393             args = args.replace("(", "\(")
394             args = args.replace(")", "\)")
395             f.write(args)
396             f.write("\n")
397             f.close()
398             if self.args["ddd_session"]:
399                 self.CMD = ["ddd", "--command=.gdbinit4salome", self.CMD[0]]
400             elif self.args["gdb_session"]:
401                 self.CMD = ["xterm", "-e", "gdb", "--command=.gdbinit4salome", self.CMD[0]]
402                 pass
403             pass
404
405         if self.args["valgrind_session"]:
406             l = ["valgrind"]
407             val = os.getenv("VALGRIND_OPTIONS")
408             if val:
409                 l += val.split()
410                 pass
411             self.CMD = l + self.CMD
412             pass
413
414 class SessionServer(CommonSessionServer):
415     def __init__(self,args,modules_list,modules_root_dir):
416         super().__init__(args,modules_list,modules_root_dir)
417         import KernelBasis
418         KernelBasis.setSSLMode(False)
419     
420     def getSessionServerExe(self):
421         return "SALOME_Session_Server"
422 # ---
423
424 class LauncherServer(Server):
425     def __init__(self,args):
426         self.args=args
427         self.initArgs()
428         self.SCMD1=['SALOME_LauncherServer']
429         self.SCMD2=[]
430         if args["gui"] :
431             if 'registry' in self.args['embedded']:
432                 self.SCMD1+=['--with','Registry',
433                              '(','--salome_session','theSession',')']
434             if 'moduleCatalog' in self.args['embedded']:
435                 self.SCMD1+=['--with','ModuleCatalog','(','-common']
436                 home_dir=os.path.expanduser("~")
437                 if home_dir is not None:
438                     self.SCMD2=['-personal',os.path.join(home_dir,'Salome','resources','CatalogModulePersonnel.xml')]
439                 self.SCMD2+=[')']
440             if 'study' in self.args['embedded']:
441                 self.SCMD2+=['--with','SALOMEDS','(',')']
442             if 'cppContainer' in self.args['embedded']:
443                 self.SCMD2+=['--with','Container','(','FactoryServer',')']
444
445     def setpath(self,modules_list,modules_root_dir):
446         list_modules = modules_list[:]
447         list_modules.reverse()
448         if self.args["gui"] :
449             list_modules = ["KERNEL", "GUI"] + list_modules
450         else :
451             list_modules = ["KERNEL"] + list_modules
452
453         cata_path=get_cata_path(list_modules,modules_root_dir)
454
455         if ("gui" in self.args) & ('moduleCatalog' in self.args['embedded']):
456             #Use '::' instead ":" because drive path with "D:\" is invalid on windows platform
457             self.CMD=self.SCMD1 + ['"' + '"::"'.join(cata_path) + '"'] + self.SCMD2
458         else:
459             self.CMD=self.SCMD1 + self.SCMD2