]> SALOME platform Git repositories - tools/sat.git/blob - commands/config.py
Salome HOME
fix -h on --products and --info
[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 platform
21 import datetime
22 import shutil
23 import gettext
24 import sys
25
26 import src
27 import src.debug as DBG
28
29 # internationalization
30 satdir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
31 gettext.install('salomeTools', os.path.join(satdir, 'src', 'i18n'))
32
33 # Define all possible option for config command :  sat config <options>
34 parser = src.options.Options()
35 parser.add_option('v', 'value', 'string', 'value',
36     _("Optional: print the value of CONFIG_VARIABLE."))
37 parser.add_option('g', 'debug', 'string', 'debug',
38     _("Optional: print the debugging mode value of CONFIG_VARIABLE."))
39 parser.add_option('e', 'edit', 'boolean', 'edit',
40     _("Optional: edit the product configuration file."))
41 parser.add_option('i', 'info', 'list2', 'info',
42     _("Optional: get information on product(s). This option accepts a comma separated list."))
43 parser.add_option('l', 'list', 'boolean', 'list',
44     _("Optional: list all available applications."))
45 parser.add_option('', 'show_patchs', 'boolean', 'show_patchs',
46     _("Optional: synthetic list of all patches used in the application"))
47 parser.add_option('', 'show_properties', 'boolean', 'show_properties',
48     _("Optional: synthetic list of all properties used in the application"))
49 parser.add_option('c', 'copy', 'boolean', 'copy',
50     _("""Optional: copy a config file to the personal config files directory.
51 \tWARNING the included files are not copied.
52 \tIf a name is given the new config file takes the given name."""))
53 parser.add_option('n', 'no_label', 'boolean', 'no_label',
54     _("Internal use: do not print labels, Works only with --value and --list."))
55 parser.add_option('', 'completion', 'boolean', 'completion',
56     _("Internal use: print only keys, works only with --value."))
57 parser.add_option('s', 'schema', 'boolean', 'schema',
58     _("Internal use."))
59
60 class ConfigOpener:
61     '''Class that helps to find an application pyconf 
62        in all the possible directories (pathList)
63     '''
64     def __init__(self, pathList):
65         '''Initialization
66         
67         :param pathList list: The list of paths where to search a pyconf.
68         '''
69         self.pathList = pathList
70
71     def __call__(self, name):
72         if os.path.isabs(name):
73             return src.pyconf.ConfigInputStream(open(name, 'rb'))
74         else:
75             return src.pyconf.ConfigInputStream( 
76                         open(os.path.join( self.get_path(name), name ), 'rb') )
77         raise IOError(_("Configuration file '%s' not found") % name)
78
79     def get_path( self, name ):
80         '''The method that returns the entire path of the pyconf searched
81         :param name str: The name of the searched pyconf.
82         '''
83         for path in self.pathList:
84             if os.path.exists(os.path.join(path, name)):
85                 return path
86         raise IOError(_("Configuration file '%s' not found") % name)
87
88 class ConfigManager:
89     '''Class that manages the read of all the configuration files of salomeTools
90     '''
91     def __init__(self, datadir=None):
92         pass
93
94     def _create_vars(self, application=None, command=None, datadir=None):
95         '''Create a dictionary that stores all information about machine,
96            user, date, repositories, etc...
97         
98         :param application str: The application for which salomeTools is called.
99         :param command str: The command that is called.
100         :param datadir str: The repository that contain external data 
101                             for salomeTools.
102         :return: The dictionary that stores all information.
103         :rtype: dict
104         '''
105         var = {}      
106         var['user'] = src.architecture.get_user()
107         var['salometoolsway'] = os.path.dirname(
108                                     os.path.dirname(os.path.abspath(__file__)))
109         var['srcDir'] = os.path.join(var['salometoolsway'], 'src')
110         var['internal_dir'] = os.path.join(var['srcDir'], 'internal_config')
111         var['sep']= os.path.sep
112         
113         # datadir has a default location
114         var['datadir'] = os.path.join(var['salometoolsway'], 'data')
115         if datadir is not None:
116             var['datadir'] = datadir
117
118         var['personalDir'] = os.path.join(os.path.expanduser('~'),
119                                            '.salomeTools')
120         src.ensure_path_exists(var['personalDir'])
121
122         var['personal_applications_dir'] = os.path.join(var['personalDir'],
123                                                         "Applications")
124         src.ensure_path_exists(var['personal_applications_dir'])
125         
126         var['personal_products_dir'] = os.path.join(var['personalDir'],
127                                                     "products")
128         src.ensure_path_exists(var['personal_products_dir'])
129         
130         var['personal_archives_dir'] = os.path.join(var['personalDir'],
131                                                     "Archives")
132         src.ensure_path_exists(var['personal_archives_dir'])
133
134         var['personal_jobs_dir'] = os.path.join(var['personalDir'],
135                                                 "Jobs")
136         src.ensure_path_exists(var['personal_jobs_dir'])
137
138         var['personal_machines_dir'] = os.path.join(var['personalDir'],
139                                                     "Machines")
140         src.ensure_path_exists(var['personal_machines_dir'])
141
142         # read linux distributions dictionary
143         distrib_cfg = src.pyconf.Config(os.path.join(var['srcDir'],
144                                                       'internal_config',
145                                                       'distrib.pyconf'))
146         
147         # set platform parameters
148         dist_name = src.architecture.get_distribution(
149                                             codes=distrib_cfg.DISTRIBUTIONS)
150         dist_version = src.architecture.get_distrib_version(dist_name, 
151                                                     codes=distrib_cfg.VERSIONS)
152         dist = dist_name + dist_version
153         
154         var['dist_name'] = dist_name
155         var['dist_version'] = dist_version
156         var['dist'] = dist
157         var['python'] = src.architecture.get_python_version()
158
159         var['nb_proc'] = src.architecture.get_nb_proc()
160         node_name = platform.node()
161         var['node'] = node_name
162         var['hostname'] = node_name
163
164         # set date parameters
165         dt = datetime.datetime.now()
166         var['date'] = dt.strftime('%Y%m%d')
167         var['datehour'] = dt.strftime('%Y%m%d_%H%M%S')
168         var['hour'] = dt.strftime('%H%M%S')
169
170         var['command'] = str(command)
171         var['application'] = str(application)
172
173         # Root dir for temporary files 
174         var['tmp_root'] = os.sep + 'tmp' + os.sep + var['user']
175         # particular win case 
176         if src.architecture.is_windows() : 
177             var['tmp_root'] =  os.path.expanduser('~') + os.sep + 'tmp'
178         
179         return var
180
181     def get_command_line_overrides(self, options, sections):
182         '''get all the overwrites that are in the command line
183         
184         :param options: the options from salomeTools class 
185                         initialization (like -l5 or --overwrite)
186         :param sections str: The config section to overwrite.
187         :return: The list of all the overwrites to apply.
188         :rtype: list
189         '''
190         # when there are no options or not the overwrite option, 
191         # return an empty list
192         if options is None or options.overwrite is None:
193             return []
194         
195         over = []
196         for section in sections:
197             # only overwrite the sections that correspond to the option 
198             over.extend(filter(lambda l: l.startswith(section + "."), 
199                                options.overwrite))
200         return over
201
202     def get_config(self, application=None, options=None, command=None,
203                     datadir=None):
204         '''get the config from all the configuration files.
205         
206         :param application str: The application for which salomeTools is called.
207         :param options class Options: The general salomeToos
208                                       options (--overwrite or -l5, for example)
209         :param command str: The command that is called.
210         :param datadir str: The repository that contain 
211                             external data for salomeTools.
212         :return: The final config.
213         :rtype: class 'src.pyconf.Config'
214         '''        
215         
216         # create a ConfigMerger to handle merge
217         merger = src.pyconf.ConfigMerger()#MergeHandler())
218         
219         # create the configuration instance
220         cfg = src.pyconf.Config()
221         
222         # =====================================================================
223         # create VARS section
224         var = self._create_vars(application=application, command=command, 
225                                 datadir=datadir)
226         # add VARS to config
227         cfg.VARS = src.pyconf.Mapping(cfg)
228         for variable in var:
229             cfg.VARS[variable] = var[variable]
230         
231         # apply overwrite from command line if needed
232         for rule in self.get_command_line_overrides(options, ["VARS"]):
233             exec('cfg.' + rule) # this cannot be factorized because of the exec
234         
235         # =====================================================================
236         # Load INTERNAL config
237         # read src/internal_config/salomeTools.pyconf
238         src.pyconf.streamOpener = ConfigOpener([
239                             os.path.join(cfg.VARS.srcDir, 'internal_config')])
240         try:
241             internal_cfg = src.pyconf.Config(open(os.path.join(cfg.VARS.srcDir, 
242                                     'internal_config', 'salomeTools.pyconf')))
243         except src.pyconf.ConfigError as e:
244             raise src.SatException(_("Error in configuration file:"
245                                      " salomeTools.pyconf\n  %(error)s") % \
246                                    {'error': str(e) })
247         
248         merger.merge(cfg, internal_cfg)
249
250         # apply overwrite from command line if needed
251         for rule in self.get_command_line_overrides(options, ["INTERNAL"]):
252             exec('cfg.' + rule) # this cannot be factorized because of the exec        
253                
254         # =====================================================================
255         # Load LOCAL config file
256         # search only in the data directory
257         src.pyconf.streamOpener = ConfigOpener([cfg.VARS.datadir])
258         try:
259             local_cfg = src.pyconf.Config(open(os.path.join(cfg.VARS.datadir, 
260                                                            'local.pyconf')),
261                                          PWD = ('LOCAL', cfg.VARS.datadir) )
262         except src.pyconf.ConfigError as e:
263             raise src.SatException(_("Error in configuration file: "
264                                      "local.pyconf\n  %(error)s") % \
265                 {'error': str(e) })
266         except IOError as error:
267             e = str(error)
268             raise src.SatException( e );
269         merger.merge(cfg, local_cfg)
270
271         # When the key is "default", put the default value
272         if cfg.LOCAL.base == "default":
273             cfg.LOCAL.base = os.path.abspath(
274                                         os.path.join(cfg.VARS.salometoolsway,
275                                                      "..",
276                                                      "BASE"))
277         if cfg.LOCAL.workdir == "default":
278             cfg.LOCAL.workdir = os.path.abspath(
279                                         os.path.join(cfg.VARS.salometoolsway,
280                                                      ".."))
281         if cfg.LOCAL.log_dir == "default":
282             cfg.LOCAL.log_dir = os.path.abspath(
283                                         os.path.join(cfg.VARS.salometoolsway,
284                                                      "..",
285                                                      "LOGS"))
286
287         if cfg.LOCAL.archive_dir == "default":
288             cfg.LOCAL.archive_dir = os.path.abspath(
289                                         os.path.join(cfg.VARS.salometoolsway,
290                                                      "..",
291                                                      "ARCHIVES"))
292
293         # apply overwrite from command line if needed
294         for rule in self.get_command_line_overrides(options, ["LOCAL"]):
295             exec('cfg.' + rule) # this cannot be factorized because of the exec
296         
297         # =====================================================================
298         # Load the PROJECTS
299         projects_cfg = src.pyconf.Config()
300         projects_cfg.addMapping("PROJECTS",
301                                 src.pyconf.Mapping(projects_cfg),
302                                 "The projects\n")
303         projects_cfg.PROJECTS.addMapping("projects",
304                                 src.pyconf.Mapping(cfg.PROJECTS),
305                                 "The projects definition\n")
306         
307         for project_pyconf_path in cfg.PROJECTS.project_file_paths:
308             if not os.path.exists(project_pyconf_path):
309                 msg = _("WARNING: The project file %s cannot be found. "
310                         "It will be ignored\n" % project_pyconf_path)
311                 sys.stdout.write(msg)
312                 continue
313             project_name = os.path.basename(
314                                     project_pyconf_path)[:-len(".pyconf")]
315             try:
316                 project_pyconf_dir = os.path.dirname(project_pyconf_path)
317                 project_cfg = src.pyconf.Config(open(project_pyconf_path),
318                                                 PWD=("", project_pyconf_dir))
319             except Exception as e:
320                 msg = _("ERROR: Error in configuration file: "
321                                  "%(file_path)s\n  %(error)s\n") % \
322                             {'file_path' : project_pyconf_path, 'error': str(e) }
323                 sys.stdout.write(msg)
324                 continue
325             projects_cfg.PROJECTS.projects.addMapping(project_name,
326                              src.pyconf.Mapping(projects_cfg.PROJECTS.projects),
327                              "The %s project\n" % project_name)
328             projects_cfg.PROJECTS.projects[project_name]=project_cfg
329             projects_cfg.PROJECTS.projects[project_name]["file_path"] = \
330                                                         project_pyconf_path
331                    
332         merger.merge(cfg, projects_cfg)
333
334         # apply overwrite from command line if needed
335         for rule in self.get_command_line_overrides(options, ["PROJECTS"]):
336             exec('cfg.' + rule) # this cannot be factorized because of the exec
337         
338         # =====================================================================
339         # Create the paths where to search the application configurations, 
340         # the product configurations, the products archives, 
341         # the jobs configurations and the machines configurations
342         cfg.addMapping("PATHS", src.pyconf.Mapping(cfg), "The paths\n")
343         cfg.PATHS["APPLICATIONPATH"] = src.pyconf.Sequence(cfg.PATHS)
344         cfg.PATHS.APPLICATIONPATH.append(cfg.VARS.personal_applications_dir, "")
345
346         
347         cfg.PATHS["PRODUCTPATH"] = src.pyconf.Sequence(cfg.PATHS)
348         cfg.PATHS.PRODUCTPATH.append(cfg.VARS.personal_products_dir, "")
349         cfg.PATHS["ARCHIVEPATH"] = src.pyconf.Sequence(cfg.PATHS)
350         cfg.PATHS.ARCHIVEPATH.append(cfg.VARS.personal_archives_dir, "")
351         cfg.PATHS["JOBPATH"] = src.pyconf.Sequence(cfg.PATHS)
352         cfg.PATHS.JOBPATH.append(cfg.VARS.personal_jobs_dir, "")
353         cfg.PATHS["MACHINEPATH"] = src.pyconf.Sequence(cfg.PATHS)
354         cfg.PATHS.MACHINEPATH.append(cfg.VARS.personal_machines_dir, "")
355
356         # initialise the path with local directory
357         cfg.PATHS["ARCHIVEPATH"].append(cfg.LOCAL.archive_dir, "")
358
359         # Loop over the projects in order to complete the PATHS variables
360         # as /data/tmpsalome/salome/prerequis/archives for example ARCHIVEPATH
361         for project in cfg.PROJECTS.projects:
362             for PATH in ["APPLICATIONPATH",
363                          "PRODUCTPATH",
364                          "ARCHIVEPATH", #comment this for default archive       #8646
365                          "JOBPATH",
366                          "MACHINEPATH"]:
367                 if PATH not in cfg.PROJECTS.projects[project]:
368                     continue
369                 cfg.PATHS[PATH].append(cfg.PROJECTS.projects[project][PATH], "")
370         
371         # apply overwrite from command line if needed
372         for rule in self.get_command_line_overrides(options, ["PATHS"]):
373             exec('cfg.' + rule) # this cannot be factorized because of the exec
374
375         # AT END append APPLI_TEST directory in APPLICATIONPATH, for unittest
376         appli_test_dir = os.path.join(satdir, "test", "APPLI_TEST")
377         if appli_test_dir not in cfg.PATHS.APPLICATIONPATH:
378           cfg.PATHS.APPLICATIONPATH.append(appli_test_dir, "unittest APPLI_TEST path")
379
380         # =====================================================================
381         # Load APPLICATION config file
382         if application is not None:
383             # search APPLICATION file in all directories in configPath
384             cp = cfg.PATHS.APPLICATIONPATH
385             src.pyconf.streamOpener = ConfigOpener(cp)
386             do_merge = True
387             try:
388                 application_cfg = src.pyconf.Config(application + '.pyconf')
389             except IOError as e:
390                 raise src.SatException(
391                    _("%s, use 'config --list' to get the list of available applications.") % e)
392             except src.pyconf.ConfigError as e:
393                 if (not ('-e' in parser.parse_args()[1]) 
394                                          or ('--edit' in parser.parse_args()[1]) 
395                                          and command == 'config'):
396                     raise src.SatException(_("Error in configuration file: "
397                                              "%(application)s.pyconf\n "
398                                              " %(error)s") % \
399                         { 'application': application, 'error': str(e) } )
400                 else:
401                     sys.stdout.write(src.printcolors.printcWarning(
402                                         "There is an error in the file"
403                                         " %s.pyconf.\n" % cfg.VARS.application))
404                     do_merge = False
405             except Exception as e:
406                 if (not ('-e' in parser.parse_args()[1]) 
407                                         or ('--edit' in parser.parse_args()[1]) 
408                                         and command == 'config'):
409                     sys.stdout.write(src.printcolors.printcWarning("%s\n" % str(e)))
410                     raise src.SatException(_("Error in configuration file:"
411                                              " %(application)s.pyconf\n") % \
412                         { 'application': application} )
413                 else:
414                     sys.stdout.write(src.printcolors.printcWarning(
415                                 "There is an error in the file"
416                                 " %s.pyconf. Opening the file with the"
417                                 " default viewer\n" % cfg.VARS.application))
418                     sys.stdout.write("The error:"
419                                  " %s\n" % src.printcolors.printcWarning(
420                                                                       str(e)))
421                     do_merge = False
422         
423             else:
424                 cfg['open_application'] = 'yes'
425
426         # =====================================================================
427         # Load product config files in PRODUCTS section
428         products_cfg = src.pyconf.Config()
429         products_cfg.addMapping("PRODUCTS",
430                                 src.pyconf.Mapping(products_cfg),
431                                 "The products\n")
432         if application is not None:
433             src.pyconf.streamOpener = ConfigOpener(cfg.PATHS.PRODUCTPATH)
434             for product_name in application_cfg.APPLICATION.products.keys():
435                 # Loop on all files that are in softsDir directory
436                 # and read their config
437                 product_file_name = product_name + ".pyconf"
438                 product_file_path = src.find_file_in_lpath(product_file_name, cfg.PATHS.PRODUCTPATH)
439                 if product_file_path:
440                     products_dir = os.path.dirname(product_file_path)
441                     try:
442                         prod_cfg = src.pyconf.Config(open(product_file_path),
443                                                      PWD=("", products_dir))
444                         prod_cfg.from_file = product_file_path
445                         products_cfg.PRODUCTS[product_name] = prod_cfg
446                     except Exception as e:
447                         msg = _(
448                             "WARNING: Error in configuration file"
449                             ": %(prod)s\n  %(error)s" % \
450                             {'prod' :  product_name, 'error': str(e) })
451                         sys.stdout.write(msg)
452             
453             merger.merge(cfg, products_cfg)
454             
455             # apply overwrite from command line if needed
456             for rule in self.get_command_line_overrides(options, ["PRODUCTS"]):
457                 exec('cfg.' + rule) # this cannot be factorized because of the exec
458             
459             if do_merge:
460                 merger.merge(cfg, application_cfg)
461
462                 # default launcher name ('salome')
463                 if ('profile' in cfg.APPLICATION and 
464                     'launcher_name' not in cfg.APPLICATION.profile):
465                     cfg.APPLICATION.profile.launcher_name = 'salome'
466
467                 # apply overwrite from command line if needed
468                 for rule in self.get_command_line_overrides(options,
469                                                              ["APPLICATION"]):
470                     # this cannot be factorized because of the exec
471                     exec('cfg.' + rule)
472             
473         # =====================================================================
474         # load USER config
475         self.set_user_config_file(cfg)
476         user_cfg_file = self.get_user_config_file()
477         user_cfg = src.pyconf.Config(open(user_cfg_file))
478         merger.merge(cfg, user_cfg)
479
480         # apply overwrite from command line if needed
481         for rule in self.get_command_line_overrides(options, ["USER"]):
482             exec('cfg.' + rule) # this cannot be factorize because of the exec
483         
484         return cfg
485
486     def set_user_config_file(self, config):
487         '''Set the user config file name and path.
488         If necessary, build it from another one or create it from scratch.
489         
490         :param config class 'src.pyconf.Config': The global config 
491                                                  (containing all pyconf).
492         '''
493         # get the expected name and path of the file
494         self.config_file_name = 'SAT.pyconf'
495         self.user_config_file_path = os.path.join(config.VARS.personalDir,
496                                                    self.config_file_name)
497         
498         # if pyconf does not exist, create it from scratch
499         if not os.path.isfile(self.user_config_file_path): 
500             self.create_config_file(config)
501     
502     def create_config_file(self, config):
503         '''This method is called when there are no user config file. 
504            It build it from scratch.
505         
506         :param config class 'src.pyconf.Config': The global config.
507         :return: the config corresponding to the file created.
508         :rtype: config class 'src.pyconf.Config'
509         '''
510         
511         cfg_name = self.get_user_config_file()
512
513         user_cfg = src.pyconf.Config()
514         #
515         user_cfg.addMapping('USER', src.pyconf.Mapping(user_cfg), "")
516
517         user_cfg.USER.addMapping('cvs_user', config.VARS.user,
518             "This is the user name used to access salome cvs base.\n")
519         user_cfg.USER.addMapping('svn_user', config.VARS.user,
520             "This is the user name used to access salome svn base.\n")
521         user_cfg.USER.addMapping('output_verbose_level', 3,
522             "This is the default output_verbose_level you want."
523             " 0=>no output, 5=>debug.\n")
524         user_cfg.USER.addMapping('publish_dir', 
525                                  os.path.join(os.path.expanduser('~'),
526                                  'websupport', 
527                                  'satreport'), 
528                                  "")
529         user_cfg.USER.addMapping('editor',
530                                  'vi', 
531                                  "This is the editor used to "
532                                  "modify configuration files\n")
533         user_cfg.USER.addMapping('browser', 
534                                  'firefox', 
535                                  "This is the browser used to "
536                                  "read html documentation\n")
537         user_cfg.USER.addMapping('pdf_viewer', 
538                                  'evince', 
539                                  "This is the pdf_viewer used "
540                                  "to read pdf documentation\n")
541 # CNC 25/10/17 : plus nécessaire a priori
542 #        user_cfg.USER.addMapping("base",
543 #                                 src.pyconf.Reference(
544 #                                            user_cfg,
545 #                                            src.pyconf.DOLLAR,
546 #                                            'workdir  + $VARS.sep + "BASE"'),
547 #                                 "The products installation base (could be "
548 #                                 "ignored if this key exists in the local.pyconf"
549 #                                 " file of salomTools).\n")
550                
551         # 
552         src.ensure_path_exists(config.VARS.personalDir)
553         src.ensure_path_exists(os.path.join(config.VARS.personalDir, 
554                                             'Applications'))
555
556         f = open(cfg_name, 'w')
557         user_cfg.__save__(f)
558         f.close()
559
560         return user_cfg   
561
562     def get_user_config_file(self):
563         '''Get the user config file
564         :return: path to the user config file.
565         :rtype: str
566         '''
567         if not self.user_config_file_path:
568             raise src.SatException(_("Error in get_user_config_file: "
569                                      "missing user config file path"))
570         return self.user_config_file_path     
571
572 def check_path(path, ext=[]):
573     '''Construct a text with the input path and "not found" if it does not
574        exist.
575     
576     :param path Str: the path to check.
577     :param ext List: An extension. Verify that the path extension 
578                      is in the list
579     :return: The string of the path with information
580     :rtype: Str
581     '''
582     # check if file exists
583     if not os.path.exists(path):
584         return "'%s'" % path + " " + src.printcolors.printcError(_(
585                                                             "** not found"))
586
587     # check extension
588     if len(ext) > 0:
589         fe = os.path.splitext(path)[1].lower()
590         if fe not in ext:
591             return "'%s'" % path + " " + src.printcolors.printcError(_(
592                                                         "** bad extension"))
593
594     return path
595
596 def show_product_info(config, name, logger):
597     '''Display on the terminal and logger information about a product.
598     
599     :param config Config: the global configuration.
600     :param name Str: The name of the product
601     :param logger Logger: The logger instance to use for the display
602     '''
603     
604     logger.write(_("%s is a product\n") % src.printcolors.printcLabel(name), 2)
605     pinfo = src.product.get_product_config(config, name)
606     
607     if "depend" in pinfo:
608         src.printcolors.print_value(logger, 
609                                     "depends on", 
610                                     ', '.join(pinfo.depend), 2)
611
612     if "opt_depend" in pinfo:
613         src.printcolors.print_value(logger, 
614                                     "optional", 
615                                     ', '.join(pinfo.opt_depend), 2)
616
617     # information on pyconf
618     logger.write("\n", 2)
619     logger.write(src.printcolors.printcLabel("configuration:") + "\n", 2)
620     if "from_file" in pinfo:
621         src.printcolors.print_value(logger, 
622                                     "pyconf file path", 
623                                     pinfo.from_file, 
624                                     2)
625     if "section" in pinfo:
626         src.printcolors.print_value(logger, 
627                                     "section", 
628                                     pinfo.section, 
629                                     2)
630
631     # information on prepare
632     logger.write("\n", 2)
633     logger.write(src.printcolors.printcLabel("prepare:") + "\n", 2)
634
635     is_dev = src.product.product_is_dev(pinfo)
636     method = pinfo.get_source
637     if is_dev:
638         method += " (dev)"
639     src.printcolors.print_value(logger, "get method", method, 2)
640
641     if method == 'cvs':
642         src.printcolors.print_value(logger, "server", pinfo.cvs_info.server, 2)
643         src.printcolors.print_value(logger, "base module",
644                                     pinfo.cvs_info.module_base, 2)
645         src.printcolors.print_value(logger, "source", pinfo.cvs_info.source, 2)
646         src.printcolors.print_value(logger, "tag", pinfo.cvs_info.tag, 2)
647
648     elif method == 'svn':
649         src.printcolors.print_value(logger, "repo", pinfo.svn_info.repo, 2)
650
651     elif method == 'git':
652         src.printcolors.print_value(logger, "repo", pinfo.git_info.repo, 2)
653         src.printcolors.print_value(logger, "tag", pinfo.git_info.tag, 2)
654
655     elif method == 'archive':
656         src.printcolors.print_value(logger, 
657                                     "get from", 
658                                     check_path(pinfo.archive_info.archive_name), 
659                                     2)
660
661     if 'patches' in pinfo:
662         for patch in pinfo.patches:
663             src.printcolors.print_value(logger, "patch", check_path(patch), 2)
664
665     if src.product.product_is_fixed(pinfo):
666         src.printcolors.print_value(logger, "install_dir", 
667                                     check_path(pinfo.install_dir), 2)
668
669     if src.product.product_is_native(pinfo) or src.product.product_is_fixed(pinfo):
670         return
671     
672     # information on compilation
673     if src.product.product_compiles(pinfo):
674         logger.write("\n", 2)
675         logger.write(src.printcolors.printcLabel("compile:") + "\n", 2)
676         src.printcolors.print_value(logger, 
677                                     "compilation method", 
678                                     pinfo.build_source, 
679                                     2)
680         
681         if pinfo.build_source == "script" and "compil_script" in pinfo:
682             src.printcolors.print_value(logger, 
683                                         "Compilation script", 
684                                         pinfo.compil_script, 
685                                         2)
686         
687         if 'nb_proc' in pinfo:
688             src.printcolors.print_value(logger, "make -j", pinfo.nb_proc, 2)
689     
690         src.printcolors.print_value(logger, 
691                                     "source dir", 
692                                     check_path(pinfo.source_dir), 
693                                     2)
694         if 'install_dir' in pinfo:
695             src.printcolors.print_value(logger, 
696                                         "build dir", 
697                                         check_path(pinfo.build_dir), 
698                                         2)
699             src.printcolors.print_value(logger, 
700                                         "install dir", 
701                                         check_path(pinfo.install_dir), 
702                                         2)
703         else:
704             logger.write("  " + 
705                          src.printcolors.printcWarning(_("no install dir")) + 
706                          "\n", 2)
707     else:
708         logger.write("\n", 2)
709         msg = _("This product does not compile")
710         logger.write("%s\n" % msg, 2)
711
712     # information on environment
713     logger.write("\n", 2)
714     logger.write(src.printcolors.printcLabel("environ :") + "\n", 2)
715     if "environ" in pinfo and "env_script" in pinfo.environ:
716         src.printcolors.print_value(logger, 
717                                     "script", 
718                                     check_path(pinfo.environ.env_script), 
719                                     2)
720
721     zz = src.environment.SalomeEnviron(config, 
722                                        src.fileEnviron.ScreenEnviron(logger), 
723                                        False)
724     zz.set_python_libdirs()
725     zz.set_a_product(name, logger)
726     logger.write("\n", 2)
727
728
729 def show_patchs(config, logger):
730   '''Prints all the used patchs in the application.
731
732   :param config Config: the global configuration.
733   :param logger Logger: The logger instance to use for the display
734   '''
735   oneOrMore = False
736   for product in sorted(config.APPLICATION.products):
737     try:
738       product_info = src.product.get_product_config(config, product)
739       if src.product.product_has_patches(product_info):
740         oneOrMore = True
741         logger.write("%s:\n" % product, 1)
742         for i in product_info.patches:
743           logger.write(src.printcolors.printcInfo("    %s\n" % i), 1)
744     except Exception as e:
745       msg = "problem on product %s\n%s\n" % (product, str(e))
746       logger.error(msg)
747
748   if oneOrMore:
749     logger.write("\n", 1)
750   else:
751     logger.write("No patchs found\n", 1)
752
753
754 def show_properties(config, logger):
755   '''Prints all the used properties in the application.
756
757   :param config Config: the global configuration.
758   :param logger Logger: The logger instance to use for the display
759   '''
760   oneOrMore = False
761   for product in sorted(config.APPLICATION.products):
762     try:
763       product_info = src.product.get_product_config(config, product)
764       done = False
765       try:
766         for i in product_info.properties:
767           if not done:
768             logger.write("%s:\n" % product, 1)
769             done = True
770           oneOrMore = True
771           logger.write(src.printcolors.printcInfo("    %s\n" % i), 1)
772       except Exception as e:
773         pass
774     except Exception as e:
775       # logger.write(src.printcolors.printcInfo("    %s\n" % "no properties"), 1)
776       msg = "problem on product %s\n%s\n" % (product, e)
777       logger.error(msg)
778
779   if oneOrMore:
780     logger.write("\n", 1)
781   else:
782     logger.write("No properties found\n", 1)
783
784 def print_value(config, path, show_label, logger, level=0, show_full_path=False):
785     '''Prints a value from the configuration. Prints recursively the values 
786        under the initial path.
787     
788     :param config class 'src.pyconf.Config': The configuration 
789                                              from which the value is displayed.
790     :param path str : the path in the configuration of the value to print.
791     :param show_label boolean: if True, do a basic display. 
792                                (useful for bash completion)
793     :param logger Logger: the logger instance
794     :param level int: The number of spaces to add before display.
795     :param show_full_path :
796     '''            
797     
798     # Make sure that the path does not ends with a point
799     if path.endswith('.'):
800         path = path[:-1]
801     
802     # display all the path or not
803     if show_full_path:
804         vname = path
805     else:
806         vname = path.split('.')[-1]
807
808     # number of spaces before the display
809     tab_level = "  " * level
810     
811     # call to the function that gets the value of the path.
812     try:
813         val = config.getByPath(path)
814     except Exception as e:
815         logger.write(tab_level)
816         logger.write("%s: ERROR %s\n" % (src.printcolors.printcLabel(vname), 
817                                          src.printcolors.printcError(str(e))))
818         return
819
820     # in this case, display only the value
821     if show_label:
822         logger.write(tab_level)
823         logger.write("%s: " % src.printcolors.printcLabel(vname))
824
825     # The case where the value has under values, 
826     # do a recursive call to the function
827     if dir(val).__contains__('keys'):
828         if show_label: logger.write("\n")
829         for v in sorted(val.keys()):
830             print_value(config, path + '.' + v, show_label, logger, level + 1)
831     elif val.__class__ == src.pyconf.Sequence or isinstance(val, list): 
832         # in this case, value is a list (or a Sequence)
833         if show_label: logger.write("\n")
834         index = 0
835         for v in val:
836             print_value(config, path + "[" + str(index) + "]", 
837                         show_label, logger, level + 1)
838             index = index + 1
839     else: # case where val is just a str
840         logger.write("%s\n" % val)
841
842 def get_config_children(config, args):
843     '''Gets the names of the children of the given parameter.
844        Useful only for completion mechanism
845     
846     :param config Config: The configuration where to read the values
847     :param args: The path in the config from which get the keys
848     '''
849     vals = []
850     rootkeys = config.keys()
851     
852     if len(args) == 0:
853         # no parameter returns list of root keys
854         vals = rootkeys
855     else:
856         parent = args[0]
857         pos = parent.rfind('.')
858         if pos < 0:
859             # Case where there is only on key as parameter.
860             # For example VARS
861             vals = [m for m in rootkeys if m.startswith(parent)]
862         else:
863             # Case where there is a part from a key
864             # for example VARS.us  (for VARS.user)
865             head = parent[0:pos]
866             tail = parent[pos+1:]
867             try:
868                 a = config.getByPath(head)
869                 if dir(a).__contains__('keys'):
870                     vals = map(lambda x: head + '.' + x,
871                                [m for m in a.keys() if m.startswith(tail)])
872             except:
873                 pass
874
875     for v in sorted(vals):
876         sys.stdout.write("%s\n" % v)
877
878 def description():
879     '''method that is called when salomeTools is called with --help option.
880     
881     :return: The text to display for the config command description.
882     :rtype: str
883     '''
884     return _("The config command allows manipulation "
885              "and operation on config files.\n\nexample:\nsat config "
886              "SALOME-master --info ParaView")
887     
888
889 def run(args, runner, logger):
890     '''method that is called when salomeTools is called with config parameter.
891     '''
892     # Parse the options
893     (options, args) = parser.parse_args(args)
894
895     # Only useful for completion mechanism : print the keys of the config
896     if options.schema:
897         get_config_children(runner.cfg, args)
898         return
899
900     # case : print a value of the config
901     if options.value:
902         if options.value == ".":
903             # if argument is ".", print all the config
904             for val in sorted(runner.cfg.keys()):
905                 print_value(runner.cfg, val, not options.no_label, logger)
906         else:
907             print_value(runner.cfg, options.value, not options.no_label, logger, 
908                         level=0, show_full_path=False)
909     
910     # case : print a debug value of the config
911     if options.debug:
912         if options.debug == ".":
913             # if argument is ".", print all the config
914             res = DBG.indent(DBG.getStrConfigDbg(runner.cfg))
915             logger.write("\nConfig of application %s:\n\n%s\n" % (runner.cfg.VARS.application, res))
916         else:
917             if options.debug[0] == ".": # accept ".PRODUCT.etc" as "PRODUCT.etc"
918               od = options.debug[1:]
919             else:
920               od = options.debug
921             exec("a = runner.cfg.%s" % od)
922             res = DBG.indent(DBG.getStrConfigDbg(a))
923             logger.write("\nConfig.%s of application %s:\n\n%s\n" % (od, runner.cfg.VARS.application, res))
924
925     
926     # case : edit user pyconf file or application file
927     if options.edit:
928         editor = runner.cfg.USER.editor
929         if ('APPLICATION' not in runner.cfg and
930                        'open_application' not in runner.cfg): # edit user pyconf
931             usercfg = os.path.join(runner.cfg.VARS.personalDir, 
932                                    'SAT.pyconf')
933             logger.write(_("Opening %s\n" % usercfg), 3)
934             src.system.show_in_editor(editor, usercfg, logger)
935         else:
936             # search for file <application>.pyconf and open it
937             for path in runner.cfg.PATHS.APPLICATIONPATH:
938                 pyconf_path = os.path.join(path, 
939                                     runner.cfg.VARS.application + ".pyconf")
940                 if os.path.exists(pyconf_path):
941                     logger.write(_("Opening %s\n" % pyconf_path), 3)
942                     src.system.show_in_editor(editor, pyconf_path, logger)
943                     break
944     
945     # case : give information about the product(s) in parameter
946     if options.info:
947       # DBG.write("products", sorted(runner.cfg.APPLICATION.products.keys()), True)
948       src.check_config_has_application(runner.cfg)
949       taggedProducts = src.getProductNames(runner.cfg, options.info, logger)
950       DBG.write("tagged products", sorted(taggedProducts))
951       for prod in sorted(taggedProducts):
952         if prod in runner.cfg.APPLICATION.products:
953           try:
954             if len(taggedProducts) > 1:
955               logger.write("#################### ", 2)
956             show_product_info(runner.cfg, prod, logger)
957           except Exception as e:
958             msg = "problem on product %s\n%s\n" % (prod, str(e))
959             logger.error(msg)
960           # return
961         else:
962           msg = _("%s is not a product of %s.\n") % \
963                 (prod, runner.cfg.VARS.application)
964           logger.warning(msg)
965           #raise Exception(msg)
966     
967     # case : copy an existing <application>.pyconf 
968     # to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
969     if options.copy:
970         # product is required
971         src.check_config_has_application( runner.cfg )
972
973         # get application file path 
974         source = runner.cfg.VARS.application + '.pyconf'
975         source_full_path = ""
976         for path in runner.cfg.PATHS.APPLICATIONPATH:
977             # ignore personal directory
978             if path == runner.cfg.VARS.personalDir:
979                 continue
980             # loop on all directories that can have pyconf applications
981             zz = os.path.join(path, source)
982             if os.path.exists(zz):
983                 source_full_path = zz
984                 break
985
986         if len(source_full_path) == 0:
987             raise src.SatException(_(
988                         "Config file for product %s not found\n") % source)
989         else:
990             if len(args) > 0:
991                 # a name is given as parameter, use it
992                 dest = args[0]
993             elif 'copy_prefix' in runner.cfg.INTERNAL.config:
994                 # use prefix
995                 dest = (runner.cfg.INTERNAL.config.copy_prefix 
996                         + runner.cfg.VARS.application)
997             else:
998                 # use same name as source
999                 dest = runner.cfg.VARS.application
1000                 
1001             # the full path
1002             dest_file = os.path.join(runner.cfg.VARS.personalDir, 
1003                                      'Applications', dest + '.pyconf')
1004             if os.path.exists(dest_file):
1005                 raise src.SatException(_("A personal application"
1006                                          " '%s' already exists") % dest)
1007             
1008             # perform the copy
1009             shutil.copyfile(source_full_path, dest_file)
1010             logger.write(_("%s has been created.\n") % dest_file)
1011     
1012     # case : display all the available pyconf applications
1013     if options.list:
1014         lproduct = list()
1015         # search in all directories that can have pyconf applications
1016         for path in runner.cfg.PATHS.APPLICATIONPATH:
1017             # print a header
1018             if not options.no_label:
1019                 logger.write("------ %s\n" % src.printcolors.printcHeader(path))
1020
1021             if not os.path.exists(path):
1022                 logger.write(src.printcolors.printcError(_(
1023                                             "Directory not found")) + "\n")
1024             else:
1025                 for f in sorted(os.listdir(path)):
1026                     # ignore file that does not ends with .pyconf
1027                     if not f.endswith('.pyconf'):
1028                         continue
1029
1030                     appliname = f[:-len('.pyconf')]
1031                     if appliname not in lproduct:
1032                         lproduct.append(appliname)
1033                         if path.startswith(runner.cfg.VARS.personalDir) \
1034                                     and not options.no_label:
1035                             logger.write("%s*\n" % appliname)
1036                         else:
1037                             logger.write("%s\n" % appliname)
1038                             
1039             logger.write("\n")
1040
1041     # case: print all the products name of the application (internal use for completion)
1042     if options.completion:
1043         for product_name in runner.cfg.APPLICATION.products.keys():
1044             logger.write("%s\n" % product_name)
1045         
1046     # case : give a synthetic view of all patches used in the application
1047     if options.show_patchs:
1048         src.check_config_has_application(runner.cfg)
1049         # Print some informations
1050         logger.write(_('Patchs of application %s\n') %
1051                     src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1052         logger.write("\n", 2, False)
1053         show_patchs(runner.cfg, logger)
1054
1055     # case : give a synthetic view of all patches used in the application
1056     if options.show_properties:
1057         src.check_config_has_application(runner.cfg)
1058         # Print some informations
1059         logger.write(_('Properties of application %s\n') %
1060                     src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1061         logger.write("\n", 2, False)
1062         show_properties(runner.cfg, logger)
1063