Salome HOME
spns #42205 [SAT][Windows] support different values for CMAKE_BUILD_TYPE - part III
[tools/sat.git] / commands / config.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2012  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 import os
20 import sys
21 import platform
22 import datetime
23 import shutil
24 import gettext
25 import pprint as PP
26
27 import src
28 import src.logger as LOG
29 import src.debug as DBG
30 import src.callerName as CALN
31
32 logger = LOG.getDefaultLogger()
33
34 verbose = False # True for debug
35
36 # internationalization
37 satdir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
38 gettext.install('salomeTools', os.path.join(satdir, 'src', 'i18n'))
39
40 # Define all possible option for config command :  sat config <options>
41 parser = src.options.Options()
42 parser.add_option('v', 'value', 'string', 'value',
43     _("Optional: print the value of CONFIG_VARIABLE."))
44 parser.add_option('g', 'debug', 'string', 'debug',
45     _("Optional: print the debugging mode value of CONFIG_VARIABLE."))
46 parser.add_option('e', 'edit', 'boolean', 'edit',
47     _("Optional: edit the product configuration file."))
48 parser.add_option('i', 'info', 'list2', 'info',
49     _("Optional: get information on product(s). This option accepts a comma separated list."))
50 parser.add_option('p', 'products', 'list2', 'products',
51     _("Optional: same as --info, for convenience."))
52 parser.add_option('l', 'list', 'boolean', 'list',
53     _("Optional: list all available applications."))
54 parser.add_option('', 'show_patchs', 'boolean', 'show_patchs',
55     _("Optional: synthetic list of all patches used in the application"))
56 parser.add_option('', 'show_dependencies', 'boolean', 'show_dependencies',
57     _("Optional: list of product dependencies in the application"))
58 parser.add_option('', 'show_install', 'boolean', 'show_install',
59     _("Optional: synthetic list of all install directories in the application"))
60 parser.add_option('', 'show_properties', 'boolean', 'show_properties',
61     _("Optional: synthetic list of all properties used in the application"))
62 parser.add_option('', 'check_system', 'boolean', 'check_system',
63     _("Optional: check if system products are installed"))
64 parser.add_option('c', 'copy', 'boolean', 'copy',
65     _("""Optional: copy a config file to the personal config files directory.
66 WARNING: the included files are not copied.
67 If a name is given the new config file takes the given name."""))
68 parser.add_option('n', 'no_label', 'boolean', 'no_label',
69     _("Internal use: do not print labels, Works only with --value and --list."))
70 parser.add_option('', 'completion', 'boolean', 'completion',
71     _("Internal use: print only keys, works only with --value."))
72 parser.add_option('s', 'schema', 'boolean', 'schema',
73     _("Internal use."))
74
75 def osJoin(*args):
76   """
77   shortcut wrapper to os.path.join
78   plus optionaly print for debug
79   """
80   res = os.path.realpath(os.path.join(*args))
81   if verbose:
82     if True: # ".pyconf" in res:
83       logger.info("osJoin %-80s in %s" % (res, CALN.caller_name(1)))
84   return res
85
86 class ConfigOpener:
87     '''Class that helps to find an application pyconf 
88        in all the possible directories (pathList)
89     '''
90     def __init__(self, pathList):
91         '''Initialization
92         
93         :param pathList list: The list of paths where to search a pyconf.
94         '''
95         self.pathList = pathList
96         if verbose:
97           for path in pathList:
98             if not os.path.isdir(path):
99               logger.warning("ConfigOpener inexisting directory: %s" % path)
100
101     def __call__(self, name):
102         if os.path.isabs(name):
103             return src.pyconf.ConfigInputStream(open(name, 'rb'))
104         else:
105             return src.pyconf.ConfigInputStream(open(osJoin(self.get_path(name), name), 'rb'))
106         raise IOError(_("Configuration file '%s' not found") % name)
107
108     def get_path( self, name ):
109         '''The method that returns the entire path of the pyconf searched
110         returns first found in self.pathList directories
111
112         :param name str: The name of the searched pyconf.
113         '''
114         for path in self.pathList:
115             if os.path.exists(osJoin(path, name)):
116                 return path
117         raise IOError(_("Configuration file '%s' not found") % name)
118
119 class ConfigManager:
120     '''Class that manages the read of all the configuration files of salomeTools
121     '''
122     def __init__(self, datadir=None):
123         pass
124
125     def _create_vars(self, application=None, command=None, datadir=None):
126         '''Create a dictionary that stores all information about machine,
127            user, date, repositories, etc...
128         
129         :param application str: The application for which salomeTools is called.
130         :param command str: The command that is called.
131         :param datadir str: The repository that contain external data 
132                             for salomeTools.
133         :return: The dictionary that stores all information.
134         :rtype: dict
135         '''
136         var = {}      
137         var['user'] = src.architecture.get_user()
138         var['salometoolsway'] = os.path.dirname( os.path.dirname(os.path.abspath(__file__)))
139         var['srcDir'] =  osJoin(var['salometoolsway'], 'src')
140         var['internal_dir'] =  osJoin(var['srcDir'], 'internal_config')
141         var['sep']= os.path.sep
142         if src.architecture.is_windows():
143           var['scriptExtension'] = '.bat'
144         else:
145           var['scriptExtension'] = '.sh'
146         
147         # datadir has a default location
148         var['datadir'] =  osJoin(var['salometoolsway'], 'data')
149         if datadir is not None:
150             var['datadir'] = datadir
151         var['personalDir'] =  osJoin(os.path.expanduser('~'), '.salomeTools')
152         src.ensure_path_exists(var['personalDir'])
153
154         var['personal_applications_dir'] =  osJoin(var['personalDir'], "Applications")
155         src.ensure_path_exists(var['personal_applications_dir'])
156         
157         var['personal_products_dir'] =  osJoin(var['personalDir'], "products")
158         src.ensure_path_exists(var['personal_products_dir'])
159         
160         var['personal_archives_dir'] =  osJoin(var['personalDir'], "Archives")
161         src.ensure_path_exists(var['personal_archives_dir'])
162
163         var['personal_jobs_dir'] =  osJoin(var['personalDir'], "Jobs")
164         src.ensure_path_exists(var['personal_jobs_dir'])
165
166         var['personal_machines_dir'] =  osJoin(var['personalDir'], "Machines")
167         src.ensure_path_exists(var['personal_machines_dir'])
168
169         # read linux distributions dictionary
170         distrib_cfg = src.pyconf.Config( osJoin(var['srcDir'], 'internal_config', 'distrib.pyconf'))
171         
172         # set platform parameters
173         dist_name = src.architecture.get_distribution(codes=distrib_cfg.DISTRIBUTIONS)
174         dist_version = src.architecture.get_distrib_version(dist_name)
175         dist_version_full = src.architecture.get_version_XY()
176         dist = dist_name + dist_version
177         
178         var['dist_name'] = dist_name
179         var['dist_version'] = dist_version
180         var['dist'] = dist
181         var['dist_ref'] = dist_name + dist_version_full
182         var['python'] = src.architecture.get_python_version()
183
184         var['nb_proc'] = src.architecture.get_nb_proc()
185         node_name = platform.node()
186         var['node'] = node_name
187         var['hostname'] = node_name
188
189         # set date parameters
190         dt = datetime.datetime.now()
191         var['date'] = dt.strftime('%Y%m%d')
192         var['datehour'] = dt.strftime('%Y%m%d_%H%M%S')
193         var['hour'] = dt.strftime('%H%M%S')
194
195         var['command'] = str(command)
196         var['application'] = str(application)
197
198         # Root dir for temporary files 
199         var['tmp_root'] = os.sep + 'tmp' + os.sep + var['user']
200         # particular win case 
201         if src.architecture.is_windows() : 
202             var['tmp_root'] =  os.path.expanduser('~') + os.sep + 'tmp'
203
204         return var
205
206     def get_command_line_overrides(self, options, sections):
207         '''get all the overwrites that are in the command line
208         
209         :param options: the options from salomeTools class 
210                         initialization (like -l5 or --overwrite)
211         :param sections str: The config section to overwrite.
212         :return: The list of all the overwrites to apply.
213         :rtype: list
214         '''
215         # when there are no options or not the overwrite option, 
216         # return an empty list
217         if options is None or options.overwrite is None:
218             return []
219         
220         over = []
221         for section in sections:
222             # only overwrite the sections that correspond to the option 
223             over.extend(filter(lambda l: l.startswith(section + "."), 
224                                options.overwrite))
225         return over
226
227     def get_config(self, application=None, options=None, command=None,
228                     datadir=None):
229         '''get the config from all the configuration files.
230         
231         :param application str: The application for which salomeTools is called.
232         :param options class Options: The general salomeToos
233                                       options (--overwrite or -l5, for example)
234         :param command str: The command that is called.
235         :param datadir str: The repository that contain 
236                             external data for salomeTools.
237         :return: The final config.
238         :rtype: class 'src.pyconf.Config'
239         '''        
240         
241         # create a ConfigMerger to handle merge
242         merger = src.pyconf.ConfigMerger()#MergeHandler())
243         
244         # create the configuration instance
245         cfg = src.pyconf.Config()
246         
247         # =====================================================================
248         # create VARS section
249         var = self._create_vars(application=application, command=command, datadir=datadir)
250         # DBG.write("create_vars", var, DBG.isDeveloper())
251
252         # add VARS to config
253         cfg.VARS = src.pyconf.Mapping(cfg)
254         for variable in var:
255             cfg.VARS[variable] = var[variable]
256         
257         # apply overwrite from command line if needed
258         for rule in self.get_command_line_overrides(options, ["VARS"]):
259             exec('cfg.' + rule) # this cannot be factorized because of the exec
260         
261         # =====================================================================
262         # Load INTERNAL config
263         # read src/internal_config/salomeTools.pyconf
264         src.pyconf.streamOpener = ConfigOpener([
265                              osJoin(cfg.VARS.srcDir, 'internal_config')])
266         try:
267             if src.architecture.is_windows(): # special internal config for windows
268                 internal_cfg = src.pyconf.Config(open( osJoin(cfg.VARS.srcDir,
269                                         'internal_config', 'salomeTools_win.pyconf')))
270             else:
271                 internal_cfg = src.pyconf.Config(open( osJoin(cfg.VARS.srcDir,
272                                         'internal_config', 'salomeTools.pyconf')))
273         except src.pyconf.ConfigError as e:
274             raise src.SatException(_("Error in configuration file:"
275                                      " salomeTools.pyconf\n  %(error)s") % \
276                                    {'error': str(e) })
277         
278         merger.merge(cfg, internal_cfg)
279
280         # apply overwrite from command line if needed
281         for rule in self.get_command_line_overrides(options, ["INTERNAL"]):
282             exec('cfg.' + rule) # this cannot be factorized because of the exec        
283                
284         # =====================================================================
285         # Load LOCAL config file
286         # search only in the data directory
287         src.pyconf.streamOpener = ConfigOpener([cfg.VARS.datadir])
288         try:
289             local_cfg = src.pyconf.Config(open( osJoin(cfg.VARS.datadir,
290                                                            'local.pyconf')),
291                                          PWD = ('LOCAL', cfg.VARS.datadir) )
292         except src.pyconf.ConfigError as e:
293             raise src.SatException(_("Error in configuration file: "
294                                      "local.pyconf\n  %(error)s") % \
295                 {'error': str(e) })
296         except IOError as error:
297             e = str(error)
298             raise src.SatException( e );
299         merger.merge(cfg, local_cfg)
300
301         # When the key is "default", put the default value
302         if cfg.LOCAL.base == "default":
303             cfg.LOCAL.base = os.path.abspath(osJoin(cfg.VARS.salometoolsway, "..", "BASE"))
304         if cfg.LOCAL.workdir == "default":
305             cfg.LOCAL.workdir = os.path.abspath(osJoin(cfg.VARS.salometoolsway, ".."))
306         if cfg.LOCAL.log_dir == "default":
307             cfg.LOCAL.log_dir = os.path.abspath(osJoin(cfg.VARS.salometoolsway, "..", "LOGS"))
308
309         if cfg.LOCAL.archive_dir == "default":
310             cfg.LOCAL.archive_dir = os.path.abspath( osJoin(cfg.VARS.salometoolsway, "..", "ARCHIVES"))
311
312         # if the sat tag was not set permanently by user
313         if cfg.LOCAL.tag == "unknown":
314             # get the tag with git, and store it
315             sat_version=src.system.git_describe(cfg.VARS.salometoolsway) 
316             if sat_version == False:
317                 sat_version=cfg.INTERNAL.sat_version
318             cfg.LOCAL.tag=sat_version
319                 
320
321         # apply overwrite from command line if needed
322         for rule in self.get_command_line_overrides(options, ["LOCAL"]):
323             exec('cfg.' + rule) # this cannot be factorized because of the exec
324         
325         # =====================================================================
326         # Load the PROJECTS
327         projects_cfg = src.pyconf.Config()
328         projects_cfg.addMapping("PROJECTS",
329                                 src.pyconf.Mapping(projects_cfg),
330                                 "The projects\n")
331         projects_cfg.PROJECTS.addMapping("projects",
332                                 src.pyconf.Mapping(cfg.PROJECTS),
333                                 "The projects definition\n")
334         
335         for project_pyconf_path in cfg.PROJECTS.project_file_paths:
336             if not os.path.isabs(project_pyconf_path):
337                 # for a relative path (archive case) we complete with sat path
338                 project_pyconf_path = os.path.join(cfg.VARS.salometoolsway,
339                                                   project_pyconf_path)
340             if not os.path.exists(project_pyconf_path):
341                 msg = _("WARNING: The project file %s cannot be found. "
342                         "It will be ignored\n" % project_pyconf_path)
343                 sys.stdout.write(msg)
344                 continue
345             project_name = os.path.basename(
346                                     project_pyconf_path)[:-len(".pyconf")]
347             try:
348                 project_pyconf_dir = os.path.dirname(project_pyconf_path)
349                 project_cfg = src.pyconf.Config(open(project_pyconf_path),
350                                                 PWD=("", project_pyconf_dir))
351             except Exception as e:
352                 msg = _("ERROR: Error in configuration file: "
353                                  "%(file_path)s\n  %(error)s\n") % \
354                             {'file_path' : project_pyconf_path, 'error': str(e) }
355                 sys.stdout.write(msg)
356                 continue
357             projects_cfg.PROJECTS.projects.addMapping(project_name,
358                              src.pyconf.Mapping(projects_cfg.PROJECTS.projects),
359                              "The %s project\n" % project_name)
360             projects_cfg.PROJECTS.projects[project_name]=project_cfg
361             projects_cfg.PROJECTS.projects[project_name]["file_path"] = \
362                                                         project_pyconf_path
363             # store the project tag if any
364             product_project_git_tag = src.system.git_describe(os.path.dirname(project_pyconf_path))
365             if product_project_git_tag:
366                 projects_cfg.PROJECTS.projects[project_name]["git_tag"] = product_project_git_tag
367             else:
368                 projects_cfg.PROJECTS.projects[project_name]["git_tag"] = "unknown"
369                    
370         merger.merge(cfg, projects_cfg)
371
372         # apply overwrite from command line if needed
373         for rule in self.get_command_line_overrides(options, ["PROJECTS"]):
374             exec('cfg.' + rule) # this cannot be factorized because of the exec
375         
376         # =====================================================================
377         # Create the paths where to search the application configurations, 
378         # the product configurations, the products archives, 
379         # the jobs configurations and the machines configurations
380         cfg.addMapping("PATHS", src.pyconf.Mapping(cfg), "The paths\n")
381         cfg.PATHS["APPLICATIONPATH"] = src.pyconf.Sequence(cfg.PATHS)
382         cfg.PATHS.APPLICATIONPATH.append(cfg.VARS.personal_applications_dir, "")
383         
384         cfg.PATHS["PRODUCTPATH"] = src.pyconf.Sequence(cfg.PATHS)
385         cfg.PATHS.PRODUCTPATH.append(cfg.VARS.personal_products_dir, "")
386         cfg.PATHS["ARCHIVEPATH"] = src.pyconf.Sequence(cfg.PATHS)
387         cfg.PATHS.ARCHIVEPATH.append(cfg.VARS.personal_archives_dir, "")
388         cfg.PATHS["ARCHIVEFTP"] = src.pyconf.Sequence(cfg.PATHS)
389         cfg.PATHS["JOBPATH"] = src.pyconf.Sequence(cfg.PATHS)
390         cfg.PATHS.JOBPATH.append(cfg.VARS.personal_jobs_dir, "")
391         cfg.PATHS["MACHINEPATH"] = src.pyconf.Sequence(cfg.PATHS)
392         cfg.PATHS.MACHINEPATH.append(cfg.VARS.personal_machines_dir, "")
393         cfg.PATHS["LICENCEPATH"] = src.pyconf.Sequence(cfg.PATHS)
394
395         # initialise the path with local directory
396         cfg.PATHS["ARCHIVEPATH"].append(cfg.LOCAL.archive_dir, "")
397
398         # Loop over the projects in order to complete the PATHS variables
399         # as /data/tmpsalome/salome/prerequis/archives for example ARCHIVEPATH
400         for project in cfg.PROJECTS.projects:
401             for PATH in ["APPLICATIONPATH",
402                          "PRODUCTPATH",
403                          "ARCHIVEPATH", #comment this for default archive       #8646
404                          "ARCHIVEFTP",
405                          "JOBPATH",
406                          "MACHINEPATH",
407                          "LICENCEPATH"]:
408                 if PATH not in cfg.PROJECTS.projects[project]:
409                     continue
410                 pathlist=cfg.PROJECTS.projects[project][PATH].split(":")
411                 for path in pathlist:
412                     cfg.PATHS[PATH].append(path, "")
413         
414         # apply overwrite from command line if needed
415         for rule in self.get_command_line_overrides(options, ["PATHS"]):
416             exec('cfg.' + rule) # this cannot be factorized because of the exec
417
418         # add git servers if any
419         cfg.addMapping("git_info", src.pyconf.Mapping(cfg), "The repositories\n")
420         cfg.VARS['git_servers'] = []
421         cfg.VARS['opensource_git_servers'] =[]
422
423         for project in cfg.PROJECTS.projects:
424           if 'git_info' not in  cfg.PROJECTS.projects[project]:
425             logger.warning("Project: {} does not have any git_info section! Please define one!")
426             continue
427           if 'git_server' in cfg.PROJECTS.projects[project]['git_info']:
428             git_servers=cfg.PROJECTS.projects[project]['git_info']['git_server']
429             for git_server in git_servers:
430               cfg.VARS['git_servers']+=[git_server]
431               if git_servers[git_server]['opensource_only'] == 'yes' :
432                 cfg.VARS['opensource_git_servers']+=[git_server]
433           if 'default_git_server_dev' in cfg.PROJECTS.projects[project]['git_info'].keys():
434             cfg.VARS['git_servers']+=['tuleap']
435             cfg.VARS['default_git_server_dev'] = cfg.PROJECTS.projects[project]['git_info']['default_git_server_dev']
436           if 'default_git_server' in cfg.PROJECTS.projects[project]['git_info'].keys():
437             cfg.VARS['git_servers']+=['gitpub']
438             cfg.VARS['opensource_git_servers']+=['gitpub']
439             cfg.VARS['default_git_server'] = cfg.PROJECTS.projects[project]['git_info']['default_git_server']
440
441         # AT END append APPLI_TEST directory in APPLICATIONPATH, for unittest
442         appli_test_dir =  osJoin(satdir, "test", "APPLI_TEST")
443         if appli_test_dir not in cfg.PATHS.APPLICATIONPATH:
444           cfg.PATHS.APPLICATIONPATH.append(appli_test_dir, "unittest APPLI_TEST path")
445         # =====================================================================
446         # Load APPLICATION config file
447         if application is not None:
448             # search APPLICATION file in all directories in configPath
449             cp = cfg.PATHS.APPLICATIONPATH
450             src.pyconf.streamOpener = ConfigOpener(cp)
451             do_merge = True
452             try:
453                 application_cfg = src.pyconf.Config(application + '.pyconf')
454             except IOError as e:
455                 raise src.SatException(
456                    _("%s, use 'config --list' to get the list of available applications.") % e)
457             except src.pyconf.ConfigError as e:
458                 if (not ('-e' in parser.parse_args()[1]) 
459                                          or ('--edit' in parser.parse_args()[1]) 
460                                          and command == 'config'):
461                     raise src.SatException(_("Error in configuration file: "
462                                              "%(application)s.pyconf\n "
463                                              " %(error)s") % \
464                         { 'application': application, 'error': str(e) } )
465                 else:
466                     sys.stdout.write(src.printcolors.printcWarning(
467                                         "There is an error in the file"
468                                         " %s.pyconf.\n" % cfg.VARS.application))
469                     do_merge = False
470             except Exception as e:
471                 if (not ('-e' in parser.parse_args()[1]) 
472                                         or ('--edit' in parser.parse_args()[1]) 
473                                         and command == 'config'):
474                     sys.stdout.write(src.printcolors.printcWarning("%s\n" % str(e)))
475                     raise src.SatException(_("Error in configuration file:"
476                                              " %(application)s.pyconf\n") % \
477                         { 'application': application} )
478                 else:
479                     sys.stdout.write(src.printcolors.printcWarning(
480                                 "There is an error in the file"
481                                 " %s.pyconf. Opening the file with the"
482                                 " default viewer\n" % cfg.VARS.application))
483                     sys.stdout.write("The error:"
484                                  " %s\n" % src.printcolors.printcWarning(
485                                                                       str(e)))
486                     do_merge = False
487         
488             else:
489                 cfg['open_application'] = 'yes'
490         # =====================================================================
491         # Load product config files in PRODUCTS section
492         products_cfg = src.pyconf.Config()
493         products_cfg.addMapping("PRODUCTS",
494                                 src.pyconf.Mapping(products_cfg),
495                                 "The products\n")
496         if application is not None:
497             src.pyconf.streamOpener = ConfigOpener(cfg.PATHS.PRODUCTPATH)
498             for product_name in application_cfg.APPLICATION.products.keys():
499                 # Loop on all files that are in softsDir directory
500                 # and read their config
501                 product_file_name = product_name + ".pyconf"
502                 product_file_path = src.find_file_in_lpath(product_file_name, cfg.PATHS.PRODUCTPATH)
503                 if product_file_path:
504                     products_dir = os.path.dirname(product_file_path)
505                     # for a relative path (archive case) we complete with sat path
506                     if not os.path.isabs(products_dir):
507                         products_dir = os.path.join(cfg.VARS.salometoolsway,
508                                                     products_dir)
509                     try:
510                         prod_cfg = src.pyconf.Config(open(product_file_path),
511                                                      PWD=("", products_dir))
512                         prod_cfg.from_file = product_file_path
513                         products_cfg.PRODUCTS[product_name] = prod_cfg
514                     except Exception as e:
515                         msg = _(
516                             "WARNING: Error in configuration file"
517                             ": %(prod)s\n  %(error)s" % \
518                             {'prod' :  product_name, 'error': str(e) })
519                         sys.stdout.write(msg)
520             
521             merger.merge(cfg, products_cfg)
522             
523             # apply overwrite from command line if needed
524             for rule in self.get_command_line_overrides(options, ["PRODUCTS"]):
525                 exec('cfg.' + rule) # this cannot be factorized because of the exec
526             
527             if do_merge:
528                 merger.merge(cfg, application_cfg)
529
530                 # default launcher name ('salome')
531                 if ('profile' in cfg.APPLICATION and 
532                     'launcher_name' not in cfg.APPLICATION.profile):
533                     cfg.APPLICATION.profile.launcher_name = 'salome'
534
535                 # apply overwrite from command line if needed
536                 for rule in self.get_command_line_overrides(options,
537                                                              ["APPLICATION"]):
538                     # this cannot be factorized because of the exec
539                     exec('cfg.' + rule)
540             
541         # =====================================================================
542         # load USER config
543         self.set_user_config_file(cfg)
544         user_cfg_file = self.get_user_config_file()
545         user_cfg = src.pyconf.Config(open(user_cfg_file))
546         merger.merge(cfg, user_cfg)
547
548         # apply overwrite from command line if needed
549         for rule in self.get_command_line_overrides(options, ["USER"]):
550             exec('cfg.' + rule) # this cannot be factorize because of the exec
551         
552         # remove application products "blacklisted" in rm_products field
553         if "APPLICATION" in cfg and "rm_products" in cfg.APPLICATION:
554             for prod_to_remove in cfg.APPLICATION.rm_products:
555                 cfg.APPLICATION.products.__delitem__(prod_to_remove)
556             # remove rm_products section after usage
557             cfg.APPLICATION.__delitem__("rm_products")
558         return cfg
559
560     def set_user_config_file(self, config):
561         '''Set the user config file name and path.
562         If necessary, build it from another one or create it from scratch.
563         
564         :param config class 'src.pyconf.Config': The global config 
565                                                  (containing all pyconf).
566         '''
567         # get the expected name and path of the file
568         self.config_file_name = 'SAT.pyconf'
569         self.user_config_file_path =  osJoin(config.VARS.personalDir, self.config_file_name)
570         
571         # if pyconf does not exist, create it from scratch
572         if not os.path.isfile(self.user_config_file_path): 
573             self.create_config_file(config)
574     
575     def create_config_file(self, config):
576         '''This method is called when there are no user config file. 
577            It build it from scratch.
578         
579         :param config class 'src.pyconf.Config': The global config.
580         :return: the config corresponding to the file created.
581         :rtype: config class 'src.pyconf.Config'
582         '''
583         
584         cfg_name = self.get_user_config_file()
585
586         user_cfg = src.pyconf.Config()
587         #
588         user_cfg.addMapping('USER', src.pyconf.Mapping(user_cfg), "")
589
590         user_cfg.USER.addMapping('cvs_user', config.VARS.user,
591             "This is the user name used to access salome cvs base.\n")
592         user_cfg.USER.addMapping('svn_user', config.VARS.user,
593             "This is the user name used to access salome svn base.\n")
594         user_cfg.USER.addMapping('output_verbose_level', 3,
595             "This is the default output_verbose_level you want."
596             " 0=>no output, 5=>debug.\n")
597         user_cfg.USER.addMapping('publish_dir', 
598                                   osJoin(os.path.expanduser('~'),
599                                  'websupport', 
600                                  'satreport'), 
601                                  "")
602         user_cfg.USER.addMapping('editor',
603                                  'vi', 
604                                  "This is the editor used to "
605                                  "modify configuration files\n")
606         user_cfg.USER.addMapping('browser', 
607                                  'firefox', 
608                                  "This is the browser used to "
609                                  "read html documentation\n")
610         user_cfg.USER.addMapping('pdf_viewer', 
611                                  'evince', 
612                                  "This is the pdf_viewer used "
613                                  "to read pdf documentation\n")
614
615         src.ensure_path_exists(config.VARS.personalDir)
616         src.ensure_path_exists( osJoin(config.VARS.personalDir,
617                                             'Applications'))
618
619         f = open(cfg_name, 'w')
620         user_cfg.__save__(f)
621         f.close()
622
623         return user_cfg   
624
625     def get_user_config_file(self):
626         '''Get the user config file
627         :return: path to the user config file.
628         :rtype: str
629         '''
630         if not self.user_config_file_path:
631             raise src.SatException(_("Error in get_user_config_file: "
632                                      "missing user config file path"))
633         return self.user_config_file_path     
634
635 def check_path(path, ext=[]):
636     '''Construct a text with the input path and "not found" if it does not
637        exist.
638     
639     :param path Str: the path to check.
640     :param ext List: An extension. Verify that the path extension 
641                      is in the list
642     :return: The string of the path with information
643     :rtype: Str
644     '''
645     # check if file exists
646     if not os.path.exists(path):
647         return "'%s'" % path + " " + src.printcolors.printcError(_(
648                                                             "** not found"))
649
650     # check extension
651     if len(ext) > 0:
652         fe = os.path.splitext(path)[1].lower()
653         if fe not in ext:
654             return "'%s'" % path + " " + src.printcolors.printcError(_(
655                                                         "** bad extension"))
656
657     return path
658
659 def show_product_info(config, name, logger):
660     '''Display on the terminal and logger information about a product.
661     
662     :param config Config: the global configuration.
663     :param name Str: The name of the product
664     :param logger Logger: The logger instance to use for the display
665     '''
666     
667     logger.write(_("%s is a product\n") % src.printcolors.printcLabel(name), 2)
668     pinfo = src.product.get_product_config(config, name)
669     
670     if "depend" in pinfo:
671         src.printcolors.print_value(logger, "depends on", sorted(pinfo.depend), 2)
672
673     if "opt_depend" in pinfo:
674         src.printcolors.print_value(logger, "optional", sorted(pinfo.opt_depend), 2)
675
676     if "build_depend" in pinfo:
677         src.printcolors.print_value(logger, "build depend on", sorted(pinfo.build_depend), 2)
678
679
680     # information on pyconf
681     logger.write("\n", 2)
682     logger.write(src.printcolors.printcLabel("configuration:") + "\n", 2)
683     if "from_file" in pinfo:
684         src.printcolors.print_value(logger,
685                                     "pyconf file path",
686                                     pinfo.from_file,
687                                     2)
688     if "section" in pinfo:
689         src.printcolors.print_value(logger,
690                                     "section",
691                                     pinfo.section,
692                                     2)
693
694     # information on prepare
695     logger.write("\n", 2)
696     logger.write(src.printcolors.printcLabel("prepare:") + "\n", 2)
697
698     is_dev = src.product.product_is_dev(pinfo)
699     method = pinfo.get_source
700     if is_dev:
701         method += " (dev)"
702     src.printcolors.print_value(logger, "get method", method, 2)
703
704     if method == 'cvs':
705         src.printcolors.print_value(logger, "server", pinfo.cvs_info.server, 2)
706         src.printcolors.print_value(logger, "base module",
707                                     pinfo.cvs_info.module_base, 2)
708         src.printcolors.print_value(logger, "source", pinfo.cvs_info.source, 2)
709         src.printcolors.print_value(logger, "tag", pinfo.cvs_info.tag, 2)
710
711     elif method == 'svn':
712         src.printcolors.print_value(logger, "repo", pinfo.svn_info.repo, 2)
713
714     elif method == 'git':
715         src.printcolors.print_value(logger, "repo", pinfo.git_info.repo, 2)
716         src.printcolors.print_value(logger, "tag", pinfo.git_info.tag, 2)
717
718     elif method == 'archive':
719         src.printcolors.print_value(logger,
720                                     "get from",
721                                     check_path(pinfo.archive_info.archive_name),
722                                     2)
723
724     if 'patches' in pinfo:
725         for patch in pinfo.patches:
726             src.printcolors.print_value(logger, "patch", check_path(patch), 2)
727
728     if src.product.product_is_fixed(pinfo):
729         src.printcolors.print_value(logger, "install_dir",
730                                     check_path(pinfo.install_dir), 2)
731
732     if src.product.product_is_native(pinfo) or src.product.product_is_fixed(pinfo):
733         return
734     
735     # information on compilation
736     if src.product.product_compiles(pinfo):
737         logger.write("\n", 2)
738         logger.write(src.printcolors.printcLabel("compile:") + "\n", 2)
739         src.printcolors.print_value(logger,
740                                     "compilation method",
741                                     pinfo.build_source,
742                                     2)
743         
744         if pinfo.build_source == "script" and "compil_script" in pinfo:
745             src.printcolors.print_value(logger, 
746                                         "Compilation script", 
747                                         pinfo.compil_script, 
748                                         2)
749         
750         if 'nb_proc' in pinfo:
751             src.printcolors.print_value(logger, "make -j", pinfo.nb_proc, 2)
752     
753         src.printcolors.print_value(logger, 
754                                     "source dir", 
755                                     check_path(pinfo.source_dir), 
756                                     2)
757         if 'install_dir' in pinfo:
758             src.printcolors.print_value(logger, 
759                                         "build dir", 
760                                         check_path(pinfo.build_dir), 
761                                         2)
762             src.printcolors.print_value(logger, 
763                                         "install dir", 
764                                         check_path(pinfo.install_dir), 
765                                         2)
766         else:
767             logger.write("  " + 
768                          src.printcolors.printcWarning(_("no install dir")) + 
769                          "\n", 2)
770
771         src.printcolors.print_value(logger, "debug ", pinfo.debug, 2)
772         src.printcolors.print_value(logger, "verbose ", pinfo.verbose, 2)
773         src.printcolors.print_value(logger, "hpc ", pinfo.hpc, 2)
774         src.printcolors.print_value(logger, "dev ", pinfo.dev, 2)
775
776     else:
777         logger.write("\n", 2)
778         msg = _("This product does not compile")
779         logger.write("%s\n" % msg, 2)
780
781     # information on environment
782     logger.write("\n", 2)
783     logger.write(src.printcolors.printcLabel("environ :") + "\n", 2)
784     if "environ" in pinfo and "env_script" in pinfo.environ:
785         src.printcolors.print_value(logger, 
786                                     "script", 
787                                     check_path(pinfo.environ.env_script), 
788                                     2)
789
790     # display run-time environment
791     zz = src.environment.SalomeEnviron(config,
792                                        src.fileEnviron.ScreenEnviron(logger), 
793                                        False)
794     zz.set_python_libdirs()
795     zz.set_a_product(name, logger)
796     logger.write("\n", 2)
797
798
799 def show_patchs(config, logger):
800   '''Prints all the used patchs in the application.
801
802   :param config Config: the global configuration.
803   :param logger Logger: The logger instance to use for the display
804   '''
805   oneOrMore = False
806   for product in sorted(config.APPLICATION.products):
807     try:
808       product_info = src.product.get_product_config(config, product)
809       if src.product.product_has_patches(product_info):
810         oneOrMore = True
811         logger.write("%s:\n" % product, 1)
812         for i in product_info.patches:
813           logger.write(src.printcolors.printcInfo("    %s\n" % i), 1)
814     except Exception as e:
815       msg = "problem on product %s\n%s\n" % (product, str(e))
816       logger.error(msg)
817
818   if oneOrMore:
819     logger.write("\n", 1)
820   else:
821     logger.write("No patchs found\n", 1)
822
823 def check_install_system(config, logger):
824   '''Check the installation of all (declared) system products
825
826   :param config Config: the global configuration.
827   :param logger Logger: The logger instance to use for the display
828   '''
829   # get the command to use for checking the system dependencies
830   # (either rmp or apt)
831   check_cmd=src.system.get_pkg_check_cmd(config.VARS.dist_name)
832   logger.write("\nCheck the system dependencies declared in the application\n",1)
833   pkgmgr=check_cmd[0]
834   run_dep_ko=[] # list of missing run time dependencies
835   build_dep_ko=[] # list of missing compile time dependencies
836   for product in sorted(config.APPLICATION.products):
837     try:
838       product_info = src.product.get_product_config(config, product)
839       if src.product.product_is_native(product_info):
840         # if the product is native, get (in two dictionnaries the runtime and compile time 
841         # system dependencies with the status (OK/KO)
842         run_pkg,build_pkg=src.product.check_system_dep(config.VARS.dist, check_cmd, product_info)
843         #logger.write("\n*** %s ***\n" % product, 1)
844         for pkg in run_pkg:
845             logger.write("\n   - "+pkg + " : " + run_pkg[pkg], 1)
846             if "KO" in run_pkg[pkg]:
847                 run_dep_ko.append(pkg)
848         for pkg in build_pkg:
849             logger.write("\n   - "+pkg + " : " + build_pkg[pkg], 1)
850             if "KO" in build_pkg[pkg]:
851                 build_dep_ko.append(pkg)
852         #  logger.write(src.printcolors.printcInfo("    %s\n" % i), 1)
853
854     except Exception as e:
855       msg = "\nproblem with the check of system prerequisite %s\n%s\n" % (product, str(e))
856       logger.error(msg)
857       raise Exception(msg)
858
859   logger.write("\n\n",1)
860   if run_dep_ko:
861       msg="Some run time system dependencies are missing!\n"+\
862           "Please install them with %s before running salome" % pkgmgr
863       logger.warning(msg)
864       logger.write("missing run time dependencies : ",1)
865       for md in run_dep_ko: 
866         logger.write(md+" ",1)
867       logger.write("\n\n")
868         
869   if build_dep_ko:
870       msg="Some compile time system dependencies are missing!\n"+\
871           "Please install them with %s before compiling salome" % pkgmgr
872       logger.warning(msg)
873       logger.write("missing compile time dependencies : ",1)
874       for md in build_dep_ko: 
875         logger.write(md+" ",1)
876       logger.write("\n\n")
877     
878
879 def show_dependencies(config, products, logger):
880     '''Prints dependencies of products in the application.
881
882     :param config Config: the global configuration.
883     :param logger Logger: The logger instance to use for the display
884     '''
885
886     from compile import get_dependencies_graph,depth_search_graph,find_path_graph
887     # Get the list of all application products, and create its dependency graph
888     all_products_infos = src.product.get_products_infos(config.APPLICATION.products,config)
889     all_products_graph=get_dependencies_graph(all_products_infos, compile_time=False)
890
891     products_list=[]
892     product_liste_name=""
893     if products is None:
894         products_list=config.APPLICATION.products
895         products_graph = all_products_graph
896     else:
897         # 1. Extend the list with all products that depends upon the given list of products
898         products_list=products
899         product_liste_name="_".join(products)
900         visited=[]
901         for p_name in products_list:
902             visited=depth_search_graph(all_products_graph, p_name, visited)
903         products_infos = src.product.get_products_infos(visited, config)
904         products_graph = get_dependencies_graph(products_infos, compile_time=False)
905
906         # 2. Extend the list with all the dependencies of the given list of products
907         children=[]
908         for n in all_products_graph:
909             # for all products (that are not in products_list):
910             # if we we find a path from the product to the product list,
911             # then we product is a child and we add it to the children list 
912             if (n not in children) and (n not in products_list):
913                 if find_path_graph(all_products_graph, n, products_list):
914                     children = children + [n]
915         products_infos_rev = src.product.get_products_infos(children, config)
916         products_graph_rev = get_dependencies_graph(products_infos_rev, compile_time=False)
917
918     logger.write("Dependency graph (python format)\n%s\n" % products_graph, 3)
919
920     gv_file_name='%s_%s_dep.gv' % (config.VARS.application,product_liste_name)
921     logger.write("\nDependency graph (graphviz format) written in file %s\n" % 
922                  src.printcolors.printcLabel(gv_file_name), 3)
923     with open(gv_file_name,"w") as f:
924         f.write("digraph G {\n")
925         for p in products_graph:
926             for dep in products_graph[p]:
927                 f.write ("\t%s -> %s\n" % (p,dep))
928         f.write("}\n")
929         
930
931     if products is not None:
932         # if a list of products was given, produce also the reverse dependencies
933         gv_revfile_name='%s_%s_rev_dep.gv' % (config.VARS.application,product_liste_name)
934         logger.write("\nReverse dependency graph (graphviz format) written in file %s\n" % 
935                  src.printcolors.printcLabel(gv_revfile_name), 3)
936         with open(gv_revfile_name,"w") as rf:
937             rf.write("digraph G {\n")
938             for p in products_graph_rev:
939                 for dep in products_graph_rev[p]:
940                     rf.write ("\t%s -> %s\n" % (p,dep))
941             rf.write("}\n")
942     
943     graph_cmd = "dot -Tpdf %s -o %s.pdf" % (gv_file_name,gv_file_name)
944     logger.write("\nTo generate a graph use dot tool : \n  %s" % 
945                  src.printcolors.printcLabel(graph_cmd), 3)
946  
947 def show_install_dir(config, logger):
948   '''Prints all the used installed directories in the application.
949
950   :param config Config: the global configuration.
951   :param logger Logger: The logger instance to use for the display
952   '''
953   for product in sorted(config.APPLICATION.products):
954     try:
955       product_info = src.product.get_product_config(config, product)
956       install_path=src.Path(product_info.install_dir)
957       if (src.product.product_is_native(product_info)):
958           install_path="Native"
959       elif (src.product.product_is_fixed(product_info)):
960           install_path+=" (Fixed)"
961       logger.write("%s : %s\n" % (product, install_path) , 1)
962     except Exception as e:
963       msg = "problem on product %s\n%s\n" % (product, str(e))
964       logger.error(msg)
965   logger.write("\n", 1)
966
967
968 def show_properties(config, logger):
969   '''Prints all the used properties in the application.
970
971   :param config Config: the global configuration.
972   :param logger Logger: The logger instance to use for the display
973   '''
974   if "properties" in config.APPLICATION:
975       # some properties are defined at application level, we display them
976       logger.write("Application properties:\n", 1)
977       for prop in config.APPLICATION.properties:
978           logger.write(src.printcolors.printcInfo("    %s : %s\n" % (prop, config.APPLICATION.properties[prop])), 1)
979   oneOrMore = False
980   for product in sorted(config.APPLICATION.products):
981     try:
982       product_info = src.product.get_product_config(config, product)
983       done = False
984       try:
985         for prop in product_info.properties:
986           if not done:
987             logger.write("%s:\n" % product, 1)
988             done = True
989           oneOrMore = True
990           logger.write(src.printcolors.printcInfo("    %s : %s\n" % (prop, product_info.properties[prop])), 1)
991       except Exception as e:
992         pass
993     except Exception as e:
994       # logger.write(src.printcolors.printcInfo("    %s\n" % "no properties"), 1)
995       msg = "problem on product %s\n%s\n" % (product, e)
996       logger.error(msg)
997
998   if oneOrMore:
999     logger.write("\n", 1)
1000   else:
1001     logger.write("No properties found\n", 1)
1002
1003 def print_value(config, path, show_label, logger, level=0, show_full_path=False):
1004     '''Prints a value from the configuration. Prints recursively the values 
1005        under the initial path.
1006     
1007     :param config class 'src.pyconf.Config': The configuration 
1008                                              from which the value is displayed.
1009     :param path str : the path in the configuration of the value to print.
1010     :param show_label boolean: if True, do a basic display. 
1011                                (useful for bash completion)
1012     :param logger Logger: the logger instance
1013     :param level int: The number of spaces to add before display.
1014     :param show_full_path :
1015     '''            
1016     
1017     # Make sure that the path does not ends with a point
1018     if path.endswith('.'):
1019         path = path[:-1]
1020     
1021     # display all the path or not
1022     if show_full_path:
1023         vname = path
1024     else:
1025         vname = path.split('.')[-1]
1026
1027     # number of spaces before the display
1028     tab_level = "  " * level
1029     
1030     # call to the function that gets the value of the path.
1031     try:
1032         val = config.getByPath(path)
1033     except Exception as e:
1034         logger.write(tab_level)
1035         logger.write("%s: ERROR %s\n" % (src.printcolors.printcLabel(vname), 
1036                                          src.printcolors.printcError(str(e))))
1037         return
1038
1039     # in this case, display only the value
1040     if show_label:
1041         logger.write(tab_level)
1042         logger.write("%s: " % src.printcolors.printcLabel(vname))
1043
1044     # The case where the value has under values, 
1045     # do a recursive call to the function
1046     if dir(val).__contains__('keys'):
1047         if show_label: logger.write("\n")
1048         for v in sorted(val.keys()):
1049             print_value(config, path + '.' + v, show_label, logger, level + 1)
1050     elif val.__class__ == src.pyconf.Sequence or isinstance(val, list): 
1051         # in this case, value is a list (or a Sequence)
1052         if show_label: logger.write("\n")
1053         index = 0
1054         for v in val:
1055             print_value(config, path + "[" + str(index) + "]", 
1056                         show_label, logger, level + 1)
1057             index = index + 1
1058     else: # case where val is just a str
1059         logger.write("%s\n" % val)
1060
1061 def get_config_children(config, args):
1062     '''Gets the names of the children of the given parameter.
1063        Useful only for completion mechanism
1064     
1065     :param config Config: The configuration where to read the values
1066     :param args: The path in the config from which get the keys
1067     '''
1068     vals = []
1069     rootkeys = config.keys()
1070     
1071     if len(args) == 0:
1072         # no parameter returns list of root keys
1073         vals = rootkeys
1074     else:
1075         parent = args[0]
1076         pos = parent.rfind('.')
1077         if pos < 0:
1078             # Case where there is only on key as parameter.
1079             # For example VARS
1080             vals = [m for m in rootkeys if m.startswith(parent)]
1081         else:
1082             # Case where there is a part from a key
1083             # for example VARS.us  (for VARS.user)
1084             head = parent[0:pos]
1085             tail = parent[pos+1:]
1086             try:
1087                 a = config.getByPath(head)
1088                 if dir(a).__contains__('keys'):
1089                     vals = map(lambda x: head + '.' + x,
1090                                [m for m in a.keys() if m.startswith(tail)])
1091             except Exception:
1092                 pass
1093
1094     for v in sorted(vals):
1095         sys.stdout.write("%s\n" % v)
1096
1097 def description():
1098     '''method that is called when salomeTools is called with --help option.
1099     
1100     :return: The text to display for the config command description.
1101     :rtype: str
1102     '''
1103     return _("The config command allows manipulation "
1104              "and operation on config files.\n\nexample:\nsat config "
1105              "SALOME-master --info ParaView")
1106     
1107
1108 def run(args, runner, logger):
1109     '''method that is called when salomeTools is called with config parameter.
1110     '''
1111     # Parse the options
1112     (options, args) = parser.parse_args(args)
1113
1114     # Only useful for completion mechanism : print the keys of the config
1115     if options.schema:
1116         get_config_children(runner.cfg, args)
1117         return
1118
1119     # case : print a value of the config
1120     if options.value:
1121         if options.value == ".":
1122             # if argument is ".", print all the config
1123             for val in sorted(runner.cfg.keys()):
1124                 print_value(runner.cfg, val, not options.no_label, logger)
1125         else:
1126             print_value(runner.cfg, options.value, not options.no_label, logger, 
1127                         level=0, show_full_path=False)
1128     
1129     # case : print a debug value of the config
1130     if options.debug:
1131         if options.debug == ".":
1132             # if argument is ".", print all the config
1133             res = DBG.indent(DBG.getStrConfigDbg(runner.cfg))
1134             logger.write("\nConfig of application %s:\n\n%s\n" % (runner.cfg.VARS.application, res))
1135         else:
1136             if options.debug[0] == ".": # accept ".PRODUCT.etc" as "PRODUCT.etc"
1137               od = options.debug[1:]
1138             else:
1139               od = options.debug
1140             try:
1141               aCode = "a = runner.cfg.%s" % od
1142               # https://stackoverflow.com/questions/15086040/behavior-of-exec-function-in-python-2-and-python-3
1143               aDict = {"runner": runner}
1144               exec(aCode, globals(), aDict)
1145               # DBG.write("globals()", globals(), True)
1146               # DBG.write("aDict", aDict, True)
1147               res = DBG.indent(DBG.getStrConfigDbg(aDict["a"]))
1148               logger.write("\nConfig.%s of application %s:\n\n%s\n" % (od, runner.cfg.VARS.application, res))
1149             except Exception as e:
1150               msg = "\nConfig.%s of application %s: Unknown pyconf key\n" % (od, runner.cfg.VARS.application)
1151               logger.write(src.printcolors.printcError(msg), 1)
1152
1153     
1154     # case : edit user pyconf file or application file
1155     if options.edit:
1156         editor = runner.cfg.USER.editor
1157         if ('APPLICATION' not in runner.cfg and
1158                        'open_application' not in runner.cfg): # edit user pyconf
1159             usercfg =  osJoin(runner.cfg.VARS.personalDir,
1160                                    'SAT.pyconf')
1161             logger.write(_("Opening %s\n" % usercfg), 3)
1162             src.system.show_in_editor(editor, usercfg, logger)
1163         else:
1164             # search for file <application>.pyconf and open it
1165             for path in runner.cfg.PATHS.APPLICATIONPATH:
1166                 pyconf_path =  osJoin(path,
1167                                     runner.cfg.VARS.application + ".pyconf")
1168                 if os.path.exists(pyconf_path):
1169                     logger.write(_("Opening %s\n" % pyconf_path), 3)
1170                     src.system.show_in_editor(editor, pyconf_path, logger)
1171                     break
1172     
1173     # case : give information about the product(s) in parameter
1174     if options.products:
1175       if options.info is not None:
1176         logger.warning('options.products %s overrides options.info %s' % (options.products, options.info))
1177       options.info = options.products
1178
1179     if options.info:
1180       # DBG.write("products", sorted(runner.cfg.APPLICATION.products.keys()), True)
1181       src.check_config_has_application(runner.cfg)
1182       taggedProducts = src.getProductNames(runner.cfg, options.info, logger)
1183       DBG.write("tagged products", sorted(taggedProducts))
1184       for prod in sorted(taggedProducts):
1185         if prod in runner.cfg.APPLICATION.products:
1186           try:
1187             if len(taggedProducts) > 1:
1188               logger.write("#################### ", 2)
1189             show_product_info(runner.cfg, prod, logger)
1190           except Exception as e:
1191             msg = "problem on product %s\n%s\n" % (prod, str(e))
1192             logger.error(msg)
1193           # return
1194         else:
1195           msg = _("%s is not a product of %s.\n") % \
1196                 (prod, runner.cfg.VARS.application)
1197           logger.warning(msg)
1198           #raise Exception(msg)
1199     
1200     # case : copy an existing <application>.pyconf 
1201     # to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
1202     if options.copy:
1203         # product is required
1204         src.check_config_has_application( runner.cfg )
1205
1206         # get application file path 
1207         source = runner.cfg.VARS.application + '.pyconf'
1208         source_full_path = ""
1209         for path in runner.cfg.PATHS.APPLICATIONPATH:
1210             # ignore personal directory
1211             if path == runner.cfg.VARS.personalDir:
1212                 continue
1213             # loop on all directories that can have pyconf applications
1214             zz =  osJoin(path, source)
1215             if os.path.exists(zz):
1216                 source_full_path = zz
1217                 break
1218
1219         if len(source_full_path) == 0:
1220             raise src.SatException(_(
1221                         "Config file for product %s not found\n") % source)
1222         else:
1223             if len(args) > 0:
1224                 # a name is given as parameter, use it
1225                 dest = args[0]
1226             elif 'copy_prefix' in runner.cfg.INTERNAL.config:
1227                 # use prefix
1228                 dest = (runner.cfg.INTERNAL.config.copy_prefix 
1229                         + runner.cfg.VARS.application)
1230             else:
1231                 # use same name as source
1232                 dest = runner.cfg.VARS.application
1233                 
1234             # the full path
1235             dest_file =  osJoin(runner.cfg.VARS.personalDir,
1236                                      'Applications', dest + '.pyconf')
1237             if os.path.exists(dest_file):
1238                 raise src.SatException(_("A personal application"
1239                                          " '%s' already exists") % dest)
1240             
1241             # perform the copy
1242             shutil.copyfile(source_full_path, dest_file)
1243             logger.write(_("%s has been created.\n") % dest_file)
1244     
1245     # case : display all the available pyconf applications
1246     if options.list:
1247         lproduct = list()
1248         # search in all directories that can have pyconf applications
1249         for path in runner.cfg.PATHS.APPLICATIONPATH:
1250             # print a header
1251             if not options.no_label:
1252                 logger.write("------ %s\n" % src.printcolors.printcHeader(path))
1253
1254             if not os.path.exists(path):
1255                 logger.write(src.printcolors.printcError(_(
1256                                             "Directory not found")) + "\n")
1257             else:
1258                 for f in sorted(os.listdir(path)):
1259                     # ignore file that does not ends with .pyconf
1260                     if not f.endswith('.pyconf'):
1261                         continue
1262
1263                     appliname = f[:-len('.pyconf')]
1264                     if appliname not in lproduct:
1265                         lproduct.append(appliname)
1266                         if path.startswith(runner.cfg.VARS.personalDir) \
1267                                     and not options.no_label:
1268                             logger.write("%s*\n" % appliname)
1269                         else:
1270                             logger.write("%s\n" % appliname)
1271                             
1272             logger.write("\n")
1273
1274     # case: print all the products name of the application (internal use for completion)
1275     if options.completion:
1276         for product_name in runner.cfg.APPLICATION.products.keys():
1277             logger.write("%s\n" % product_name)
1278         
1279     # case : give a synthetic view of all patches used in the application
1280     if options.show_patchs:
1281         src.check_config_has_application(runner.cfg)
1282         # Print some informations
1283         logger.write(_('Patchs of application %s\n') %
1284                     src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1285         logger.write("\n", 2, False)
1286         show_patchs(runner.cfg, logger)
1287
1288     # case : give a synthetic view of all install directories used in the application
1289     if options.show_install:
1290         src.check_config_has_application(runner.cfg)
1291         # Print some informations
1292         logger.write(_('Installation directories of application %s\n') %
1293                     src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1294         logger.write("\n", 2, False)
1295         show_install_dir(runner.cfg, logger)
1296
1297     # case : give a synthetic view of all dependencies between products of the application
1298     if options.show_dependencies:
1299         src.check_config_has_application(runner.cfg)
1300         # Print some informations
1301         logger.write(_('List of run-time dependencies of the application %s, product by product\n') %
1302                     src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1303         logger.write("\n", 2, False)
1304         show_dependencies(runner.cfg, options.products, logger)
1305
1306     # case : give a synthetic view of all patches used in the application
1307     if options.show_properties:
1308         src.check_config_has_application(runner.cfg)
1309
1310         # Print some informations
1311         logger.write(_('Properties of application %s\n') %
1312                     src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1313         logger.write("\n", 2, False)
1314         show_properties(runner.cfg, logger)
1315
1316     # check system prerequisites
1317     if options.check_system:
1318        check_install_system(runner.cfg, logger)
1319        pass