Salome HOME
merge from master 8B7B
[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', '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         
345         cfg.PATHS["PRODUCTPATH"] = src.pyconf.Sequence(cfg.PATHS)
346         cfg.PATHS.PRODUCTPATH.append(cfg.VARS.personal_products_dir, "")
347         cfg.PATHS["ARCHIVEPATH"] = src.pyconf.Sequence(cfg.PATHS)
348         cfg.PATHS.ARCHIVEPATH.append(cfg.VARS.personal_archives_dir, "")
349         cfg.PATHS["JOBPATH"] = src.pyconf.Sequence(cfg.PATHS)
350         cfg.PATHS.JOBPATH.append(cfg.VARS.personal_jobs_dir, "")
351         cfg.PATHS["MACHINEPATH"] = src.pyconf.Sequence(cfg.PATHS)
352         cfg.PATHS.MACHINEPATH.append(cfg.VARS.personal_machines_dir, "")
353
354         # initialise the path with local directory
355         cfg.PATHS["ARCHIVEPATH"].append(cfg.LOCAL.archive_dir, "")
356
357         # Loop over the projects in order to complete the PATHS variables
358         # as /data/tmpsalome/salome/prerequis/archives for example ARCHIVEPATH
359         for project in cfg.PROJECTS.projects:
360             for PATH in ["APPLICATIONPATH",
361                          "PRODUCTPATH",
362                          "ARCHIVEPATH", #comment this for default archive       #8646
363                          "JOBPATH",
364                          "MACHINEPATH"]:
365                 if PATH not in cfg.PROJECTS.projects[project]:
366                     continue
367                 cfg.PATHS[PATH].append(cfg.PROJECTS.projects[project][PATH], "")
368         
369         # apply overwrite from command line if needed
370         for rule in self.get_command_line_overrides(options, ["PATHS"]):
371             exec('cfg.' + rule) # this cannot be factorized because of the exec
372
373         # AT END append APPLI_TEST directory in APPLICATIONPATH, for unittest
374         appli_test_dir = os.path.join(satdir, "test", "APPLI_TEST")
375         if appli_test_dir not in cfg.PATHS.APPLICATIONPATH:
376           cfg.PATHS.APPLICATIONPATH.append(appli_test_dir, "unittest APPLI_TEST path")
377
378         # =====================================================================
379         # Load APPLICATION config file
380         if application is not None:
381             # search APPLICATION file in all directories in configPath
382             cp = cfg.PATHS.APPLICATIONPATH
383             src.pyconf.streamOpener = ConfigOpener(cp)
384             do_merge = True
385             try:
386                 application_cfg = src.pyconf.Config(application + '.pyconf')
387             except IOError as e:
388                 raise src.SatException(
389                    _("%s, use 'config --list' to get the list of available applications.") % e)
390             except src.pyconf.ConfigError as e:
391                 if (not ('-e' in parser.parse_args()[1]) 
392                                          or ('--edit' in parser.parse_args()[1]) 
393                                          and command == 'config'):
394                     raise src.SatException(_("Error in configuration file: "
395                                              "%(application)s.pyconf\n "
396                                              " %(error)s") % \
397                         { 'application': application, 'error': str(e) } )
398                 else:
399                     sys.stdout.write(src.printcolors.printcWarning(
400                                         "There is an error in the file"
401                                         " %s.pyconf.\n" % cfg.VARS.application))
402                     do_merge = False
403             except Exception as e:
404                 if (not ('-e' in parser.parse_args()[1]) 
405                                         or ('--edit' in parser.parse_args()[1]) 
406                                         and command == 'config'):
407                     sys.stdout.write(src.printcolors.printcWarning("%s\n" % str(e)))
408                     raise src.SatException(_("Error in configuration file:"
409                                              " %(application)s.pyconf\n") % \
410                         { 'application': application} )
411                 else:
412                     sys.stdout.write(src.printcolors.printcWarning(
413                                 "There is an error in the file"
414                                 " %s.pyconf. Opening the file with the"
415                                 " default viewer\n" % cfg.VARS.application))
416                     sys.stdout.write("The error:"
417                                  " %s\n" % src.printcolors.printcWarning(
418                                                                       str(e)))
419                     do_merge = False
420         
421             else:
422                 cfg['open_application'] = 'yes'
423
424         # =====================================================================
425         # Load product config files in PRODUCTS section
426         products_cfg = src.pyconf.Config()
427         products_cfg.addMapping("PRODUCTS",
428                                 src.pyconf.Mapping(products_cfg),
429                                 "The products\n")
430         if application is not None:
431             src.pyconf.streamOpener = ConfigOpener(cfg.PATHS.PRODUCTPATH)
432             for product_name in application_cfg.APPLICATION.products.keys():
433                 # Loop on all files that are in softsDir directory
434                 # and read their config
435                 product_file_name = product_name + ".pyconf"
436                 product_file_path = src.find_file_in_lpath(product_file_name, cfg.PATHS.PRODUCTPATH)
437                 if product_file_path:
438                     products_dir = os.path.dirname(product_file_path)
439                     try:
440                         prod_cfg = src.pyconf.Config(open(product_file_path),
441                                                      PWD=("", products_dir))
442                         prod_cfg.from_file = product_file_path
443                         products_cfg.PRODUCTS[product_name] = prod_cfg
444                     except Exception as e:
445                         msg = _(
446                             "WARNING: Error in configuration file"
447                             ": %(prod)s\n  %(error)s" % \
448                             {'prod' :  product_name, 'error': str(e) })
449                         sys.stdout.write(msg)
450             
451             merger.merge(cfg, products_cfg)
452             
453             # apply overwrite from command line if needed
454             for rule in self.get_command_line_overrides(options, ["PRODUCTS"]):
455                 exec('cfg.' + rule) # this cannot be factorized because of the exec
456             
457             if do_merge:
458                 merger.merge(cfg, application_cfg)
459
460                 # default launcher name ('salome')
461                 if ('profile' in cfg.APPLICATION and 
462                     'launcher_name' not in cfg.APPLICATION.profile):
463                     cfg.APPLICATION.profile.launcher_name = 'salome'
464
465                 # apply overwrite from command line if needed
466                 for rule in self.get_command_line_overrides(options,
467                                                              ["APPLICATION"]):
468                     # this cannot be factorized because of the exec
469                     exec('cfg.' + rule)
470             
471         # =====================================================================
472         # load USER config
473         self.set_user_config_file(cfg)
474         user_cfg_file = self.get_user_config_file()
475         user_cfg = src.pyconf.Config(open(user_cfg_file))
476         merger.merge(cfg, user_cfg)
477
478         # apply overwrite from command line if needed
479         for rule in self.get_command_line_overrides(options, ["USER"]):
480             exec('cfg.' + rule) # this cannot be factorize because of the exec
481         
482         return cfg
483
484     def set_user_config_file(self, config):
485         '''Set the user config file name and path.
486         If necessary, build it from another one or create it from scratch.
487         
488         :param config class 'src.pyconf.Config': The global config 
489                                                  (containing all pyconf).
490         '''
491         # get the expected name and path of the file
492         self.config_file_name = 'SAT.pyconf'
493         self.user_config_file_path = os.path.join(config.VARS.personalDir,
494                                                    self.config_file_name)
495         
496         # if pyconf does not exist, create it from scratch
497         if not os.path.isfile(self.user_config_file_path): 
498             self.create_config_file(config)
499     
500     def create_config_file(self, config):
501         '''This method is called when there are no user config file. 
502            It build it from scratch.
503         
504         :param config class 'src.pyconf.Config': The global config.
505         :return: the config corresponding to the file created.
506         :rtype: config class 'src.pyconf.Config'
507         '''
508         
509         cfg_name = self.get_user_config_file()
510
511         user_cfg = src.pyconf.Config()
512         #
513         user_cfg.addMapping('USER', src.pyconf.Mapping(user_cfg), "")
514
515         user_cfg.USER.addMapping('cvs_user', config.VARS.user,
516             "This is the user name used to access salome cvs base.\n")
517         user_cfg.USER.addMapping('svn_user', config.VARS.user,
518             "This is the user name used to access salome svn base.\n")
519         user_cfg.USER.addMapping('output_verbose_level', 3,
520             "This is the default output_verbose_level you want."
521             " 0=>no output, 5=>debug.\n")
522         user_cfg.USER.addMapping('publish_dir', 
523                                  os.path.join(os.path.expanduser('~'),
524                                  'websupport', 
525                                  'satreport'), 
526                                  "")
527         user_cfg.USER.addMapping('editor',
528                                  'vi', 
529                                  "This is the editor used to "
530                                  "modify configuration files\n")
531         user_cfg.USER.addMapping('browser', 
532                                  'firefox', 
533                                  "This is the browser used to "
534                                  "read html documentation\n")
535         user_cfg.USER.addMapping('pdf_viewer', 
536                                  'evince', 
537                                  "This is the pdf_viewer used "
538                                  "to read pdf documentation\n")
539 # CNC 25/10/17 : plus nécessaire a priori
540 #        user_cfg.USER.addMapping("base",
541 #                                 src.pyconf.Reference(
542 #                                            user_cfg,
543 #                                            src.pyconf.DOLLAR,
544 #                                            'workdir  + $VARS.sep + "BASE"'),
545 #                                 "The products installation base (could be "
546 #                                 "ignored if this key exists in the local.pyconf"
547 #                                 " file of salomTools).\n")
548                
549         # 
550         src.ensure_path_exists(config.VARS.personalDir)
551         src.ensure_path_exists(os.path.join(config.VARS.personalDir, 
552                                             'Applications'))
553
554         f = open(cfg_name, 'w')
555         user_cfg.__save__(f)
556         f.close()
557
558         return user_cfg   
559
560     def get_user_config_file(self):
561         '''Get the user config file
562         :return: path to the user config file.
563         :rtype: str
564         '''
565         if not self.user_config_file_path:
566             raise src.SatException(_("Error in get_user_config_file: "
567                                      "missing user config file path"))
568         return self.user_config_file_path     
569
570 def check_path(path, ext=[]):
571     '''Construct a text with the input path and "not found" if it does not
572        exist.
573     
574     :param path Str: the path to check.
575     :param ext List: An extension. Verify that the path extension 
576                      is in the list
577     :return: The string of the path with information
578     :rtype: Str
579     '''
580     # check if file exists
581     if not os.path.exists(path):
582         return "'%s'" % path + " " + src.printcolors.printcError(_(
583                                                             "** not found"))
584
585     # check extension
586     if len(ext) > 0:
587         fe = os.path.splitext(path)[1].lower()
588         if fe not in ext:
589             return "'%s'" % path + " " + src.printcolors.printcError(_(
590                                                         "** bad extension"))
591
592     return path
593
594 def show_product_info(config, name, logger):
595     '''Display on the terminal and logger information about a product.
596     
597     :param config Config: the global configuration.
598     :param name Str: The name of the product
599     :param logger Logger: The logger instance to use for the display
600     '''
601     
602     logger.write(_("%s is a product\n") % src.printcolors.printcLabel(name), 2)
603     pinfo = src.product.get_product_config(config, name)
604     
605     if "depend" in pinfo:
606         src.printcolors.print_value(logger, 
607                                     "depends on", 
608                                     ', '.join(pinfo.depend), 2)
609
610     if "opt_depend" in pinfo:
611         src.printcolors.print_value(logger, 
612                                     "optional", 
613                                     ', '.join(pinfo.opt_depend), 2)
614
615     # information on pyconf
616     logger.write("\n", 2)
617     logger.write(src.printcolors.printcLabel("configuration:") + "\n", 2)
618     if "from_file" in pinfo:
619         src.printcolors.print_value(logger, 
620                                     "pyconf file path", 
621                                     pinfo.from_file, 
622                                     2)
623     if "section" in pinfo:
624         src.printcolors.print_value(logger, 
625                                     "section", 
626                                     pinfo.section, 
627                                     2)
628
629     # information on prepare
630     logger.write("\n", 2)
631     logger.write(src.printcolors.printcLabel("prepare:") + "\n", 2)
632
633     is_dev = src.product.product_is_dev(pinfo)
634     method = pinfo.get_source
635     if is_dev:
636         method += " (dev)"
637     src.printcolors.print_value(logger, "get method", method, 2)
638
639     if method == 'cvs':
640         src.printcolors.print_value(logger, "server", pinfo.cvs_info.server, 2)
641         src.printcolors.print_value(logger, "base module",
642                                     pinfo.cvs_info.module_base, 2)
643         src.printcolors.print_value(logger, "source", pinfo.cvs_info.source, 2)
644         src.printcolors.print_value(logger, "tag", pinfo.cvs_info.tag, 2)
645
646     elif method == 'svn':
647         src.printcolors.print_value(logger, "repo", pinfo.svn_info.repo, 2)
648
649     elif method == 'git':
650         src.printcolors.print_value(logger, "repo", pinfo.git_info.repo, 2)
651         src.printcolors.print_value(logger, "tag", pinfo.git_info.tag, 2)
652
653     elif method == 'archive':
654         src.printcolors.print_value(logger, 
655                                     "get from", 
656                                     check_path(pinfo.archive_info.archive_name), 
657                                     2)
658
659     if 'patches' in pinfo:
660         for patch in pinfo.patches:
661             src.printcolors.print_value(logger, "patch", check_path(patch), 2)
662
663     if src.product.product_is_fixed(pinfo):
664         src.printcolors.print_value(logger, "install_dir", 
665                                     check_path(pinfo.install_dir), 2)
666
667     if src.product.product_is_native(pinfo) or src.product.product_is_fixed(pinfo):
668         return
669     
670     # information on compilation
671     if src.product.product_compiles(pinfo):
672         logger.write("\n", 2)
673         logger.write(src.printcolors.printcLabel("compile:") + "\n", 2)
674         src.printcolors.print_value(logger, 
675                                     "compilation method", 
676                                     pinfo.build_source, 
677                                     2)
678         
679         if pinfo.build_source == "script" and "compil_script" in pinfo:
680             src.printcolors.print_value(logger, 
681                                         "Compilation script", 
682                                         pinfo.compil_script, 
683                                         2)
684         
685         if 'nb_proc' in pinfo:
686             src.printcolors.print_value(logger, "make -j", pinfo.nb_proc, 2)
687     
688         src.printcolors.print_value(logger, 
689                                     "source dir", 
690                                     check_path(pinfo.source_dir), 
691                                     2)
692         if 'install_dir' in pinfo:
693             src.printcolors.print_value(logger, 
694                                         "build dir", 
695                                         check_path(pinfo.build_dir), 
696                                         2)
697             src.printcolors.print_value(logger, 
698                                         "install dir", 
699                                         check_path(pinfo.install_dir), 
700                                         2)
701         else:
702             logger.write("  " + 
703                          src.printcolors.printcWarning(_("no install dir")) + 
704                          "\n", 2)
705     else:
706         logger.write("\n", 2)
707         msg = _("This product does not compile")
708         logger.write("%s\n" % msg, 2)
709
710     # information on environment
711     logger.write("\n", 2)
712     logger.write(src.printcolors.printcLabel("environ :") + "\n", 2)
713     if "environ" in pinfo and "env_script" in pinfo.environ:
714         src.printcolors.print_value(logger, 
715                                     "script", 
716                                     check_path(pinfo.environ.env_script), 
717                                     2)
718
719     zz = src.environment.SalomeEnviron(config, 
720                                        src.fileEnviron.ScreenEnviron(logger), 
721                                        False)
722     zz.set_python_libdirs()
723     zz.set_a_product(name, logger)
724         
725 def show_patchs(config, logger):
726     '''Prints all the used patchs in the application.
727     
728     :param config Config: the global configuration.
729     :param logger Logger: The logger instance to use for the display
730     '''
731     len_max = max([len(p) for p in config.APPLICATION.products]) + 2
732     for product in config.APPLICATION.products:
733         product_info = src.product.get_product_config(config, product)
734         if src.product.product_has_patches(product_info):
735             logger.write("%s: " % product, 1)
736             logger.write(src.printcolors.printcInfo(
737                                             " " * (len_max - len(product) -2) +
738                                             "%s\n" % product_info.patches[0]),
739                          1)
740             if len(product_info.patches) > 1:
741                 for patch in product_info.patches[1:]:
742                     logger.write(src.printcolors.printcInfo(len_max*" " +
743                                                             "%s\n" % patch), 1)
744             logger.write("\n", 1)
745
746 def print_value(config, path, show_label, logger, level=0, show_full_path=False):
747     '''Prints a value from the configuration. Prints recursively the values 
748        under the initial path.
749     
750     :param config class 'src.pyconf.Config': The configuration 
751                                              from which the value is displayed.
752     :param path str : the path in the configuration of the value to print.
753     :param show_label boolean: if True, do a basic display. 
754                                (useful for bash completion)
755     :param logger Logger: the logger instance
756     :param level int: The number of spaces to add before display.
757     :param show_full_path :
758     '''            
759     
760     # Make sure that the path does not ends with a point
761     if path.endswith('.'):
762         path = path[:-1]
763     
764     # display all the path or not
765     if show_full_path:
766         vname = path
767     else:
768         vname = path.split('.')[-1]
769
770     # number of spaces before the display
771     tab_level = "  " * level
772     
773     # call to the function that gets the value of the path.
774     try:
775         val = config.getByPath(path)
776     except Exception as e:
777         logger.write(tab_level)
778         logger.write("%s: ERROR %s\n" % (src.printcolors.printcLabel(vname), 
779                                          src.printcolors.printcError(str(e))))
780         return
781
782     # in this case, display only the value
783     if show_label:
784         logger.write(tab_level)
785         logger.write("%s: " % src.printcolors.printcLabel(vname))
786
787     # The case where the value has under values, 
788     # do a recursive call to the function
789     if dir(val).__contains__('keys'):
790         if show_label: logger.write("\n")
791         for v in sorted(val.keys()):
792             print_value(config, path + '.' + v, show_label, logger, level + 1)
793     elif val.__class__ == src.pyconf.Sequence or isinstance(val, list): 
794         # in this case, value is a list (or a Sequence)
795         if show_label: logger.write("\n")
796         index = 0
797         for v in val:
798             print_value(config, path + "[" + str(index) + "]", 
799                         show_label, logger, level + 1)
800             index = index + 1
801     else: # case where val is just a str
802         logger.write("%s\n" % val)
803
804 def get_config_children(config, args):
805     '''Gets the names of the children of the given parameter.
806        Useful only for completion mechanism
807     
808     :param config Config: The configuration where to read the values
809     :param args: The path in the config from which get the keys
810     '''
811     vals = []
812     rootkeys = config.keys()
813     
814     if len(args) == 0:
815         # no parameter returns list of root keys
816         vals = rootkeys
817     else:
818         parent = args[0]
819         pos = parent.rfind('.')
820         if pos < 0:
821             # Case where there is only on key as parameter.
822             # For example VARS
823             vals = [m for m in rootkeys if m.startswith(parent)]
824         else:
825             # Case where there is a part from a key
826             # for example VARS.us  (for VARS.user)
827             head = parent[0:pos]
828             tail = parent[pos+1:]
829             try:
830                 a = config.getByPath(head)
831                 if dir(a).__contains__('keys'):
832                     vals = map(lambda x: head + '.' + x,
833                                [m for m in a.keys() if m.startswith(tail)])
834             except:
835                 pass
836
837     for v in sorted(vals):
838         sys.stdout.write("%s\n" % v)
839
840 def description():
841     '''method that is called when salomeTools is called with --help option.
842     
843     :return: The text to display for the config command description.
844     :rtype: str
845     '''
846     return _("The config command allows manipulation "
847              "and operation on config files.\n\nexample:\nsat config "
848              "SALOME-master --info ParaView")
849     
850
851 def run(args, runner, logger):
852     '''method that is called when salomeTools is called with config parameter.
853     '''
854     # Parse the options
855     (options, args) = parser.parse_args(args)
856
857     # Only useful for completion mechanism : print the keys of the config
858     if options.schema:
859         get_config_children(runner.cfg, args)
860         return
861     
862     # case : print a value of the config
863     if options.value:
864         if options.value == ".":
865             # if argument is ".", print all the config
866             for val in sorted(runner.cfg.keys()):
867                 print_value(runner.cfg, val, not options.no_label, logger)
868         else:
869             print_value(runner.cfg, options.value, not options.no_label, logger, 
870                         level=0, show_full_path=False)
871     
872     # case : print a debug value of the config
873     if options.debug:
874         if options.debug == ".":
875             # if argument is ".", print all the config
876             res = DBG.indent(DBG.getStrConfigDbg(runner.cfg))
877             logger.write("\nConfig of application %s:\n\n%s\n" % (runner.cfg.VARS.application, res))
878         else:
879             exec("a = runner.cfg.%s" % options.debug)
880             res = DBG.indent(DBG.getStrConfigDbg(a))
881             logger.write("\nConfig.%s of application %s:\n\n%s\n" % (options.debug, runner.cfg.VARS.application, res))
882
883     
884     # case : edit user pyconf file or application file
885     elif options.edit:
886         editor = runner.cfg.USER.editor
887         if ('APPLICATION' not in runner.cfg and
888                        'open_application' not in runner.cfg): # edit user pyconf
889             usercfg = os.path.join(runner.cfg.VARS.personalDir, 
890                                    'SAT.pyconf')
891             logger.write(_("Opening %s\n" % usercfg), 3)
892             src.system.show_in_editor(editor, usercfg, logger)
893         else:
894             # search for file <application>.pyconf and open it
895             for path in runner.cfg.PATHS.APPLICATIONPATH:
896                 pyconf_path = os.path.join(path, 
897                                     runner.cfg.VARS.application + ".pyconf")
898                 if os.path.exists(pyconf_path):
899                     logger.write(_("Opening %s\n" % pyconf_path), 3)
900                     src.system.show_in_editor(editor, pyconf_path, logger)
901                     break
902     
903     # case : give information about the product in parameter
904     elif options.info:
905         src.check_config_has_application(runner.cfg)
906         if options.info in runner.cfg.APPLICATION.products:
907             show_product_info(runner.cfg, options.info, logger)
908             return
909         raise src.SatException(_("%(product_name)s is not a product "
910                                  "of %(application_name)s.") % 
911                                {'product_name' : options.info,
912                                 'application_name' : 
913                                 runner.cfg.VARS.application})
914     
915     # case : copy an existing <application>.pyconf 
916     # to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
917     elif options.copy:
918         # product is required
919         src.check_config_has_application( runner.cfg )
920
921         # get application file path 
922         source = runner.cfg.VARS.application + '.pyconf'
923         source_full_path = ""
924         for path in runner.cfg.PATHS.APPLICATIONPATH:
925             # ignore personal directory
926             if path == runner.cfg.VARS.personalDir:
927                 continue
928             # loop on all directories that can have pyconf applications
929             zz = os.path.join(path, source)
930             if os.path.exists(zz):
931                 source_full_path = zz
932                 break
933
934         if len(source_full_path) == 0:
935             raise src.SatException(_(
936                         "Config file for product %s not found\n") % source)
937         else:
938             if len(args) > 0:
939                 # a name is given as parameter, use it
940                 dest = args[0]
941             elif 'copy_prefix' in runner.cfg.INTERNAL.config:
942                 # use prefix
943                 dest = (runner.cfg.INTERNAL.config.copy_prefix 
944                         + runner.cfg.VARS.application)
945             else:
946                 # use same name as source
947                 dest = runner.cfg.VARS.application
948                 
949             # the full path
950             dest_file = os.path.join(runner.cfg.VARS.personalDir, 
951                                      'Applications', dest + '.pyconf')
952             if os.path.exists(dest_file):
953                 raise src.SatException(_("A personal application"
954                                          " '%s' already exists") % dest)
955             
956             # perform the copy
957             shutil.copyfile(source_full_path, dest_file)
958             logger.write(_("%s has been created.\n") % dest_file)
959     
960     # case : display all the available pyconf applications
961     elif options.list:
962         lproduct = list()
963         # search in all directories that can have pyconf applications
964         for path in runner.cfg.PATHS.APPLICATIONPATH:
965             # print a header
966             if not options.no_label:
967                 logger.write("------ %s\n" % src.printcolors.printcHeader(path))
968
969             if not os.path.exists(path):
970                 logger.write(src.printcolors.printcError(_(
971                                             "Directory not found")) + "\n")
972             else:
973                 for f in sorted(os.listdir(path)):
974                     # ignore file that does not ends with .pyconf
975                     if not f.endswith('.pyconf'):
976                         continue
977
978                     appliname = f[:-len('.pyconf')]
979                     if appliname not in lproduct:
980                         lproduct.append(appliname)
981                         if path.startswith(runner.cfg.VARS.personalDir) \
982                                     and not options.no_label:
983                             logger.write("%s*\n" % appliname)
984                         else:
985                             logger.write("%s\n" % appliname)
986                             
987             logger.write("\n")
988     # case : give a synthetic view of all patches used in the application
989     elif options.show_patchs:
990         src.check_config_has_application(runner.cfg)
991         # Print some informations
992         logger.write(_('Show the patchs of application %s\n') % 
993                     src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
994         logger.write("\n", 2, False)
995         show_patchs(runner.cfg, logger)
996     
997     # case: print all the products name of the application (internal use for completion)
998     elif options.completion:
999         for product_name in runner.cfg.APPLICATION.products.keys():
1000             logger.write("%s\n" % product_name)
1001         
1002