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