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