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