3 # Copyright (C) 2010-2012 CEA/DEN
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.
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.
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
27 import src.logger as LOG
28 import src.debug as DBG
29 import src.callerName as CALN
31 logger = LOG.getDefaultLogger()
33 verbose = False # True for debug
35 # internationalization
36 satdir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
37 gettext.install("salomeTools", os.path.join(satdir, "src", "i18n"))
39 # Define all possible option for config command : sat config <options>
40 parser = src.options.Options()
42 "v", "value", "string", "value", _("Optional: print the value of CONFIG_VARIABLE.")
49 _("Optional: print the debugging mode value of CONFIG_VARIABLE."),
52 "e", "edit", "boolean", "edit", _("Optional: edit the product configuration file.")
60 "Optional: get information on product(s). This option accepts a comma separated list."
68 _("Optional: same as --info, for convenience."),
71 "l", "list", "boolean", "list", _("Optional: list all available applications.")
78 _("Optional: synthetic list of all patches used in the application"),
85 _("Optional: list of product dependencies in the application"),
92 _("Optional: synthetic list of all install directories in the application"),
99 _("Optional: synthetic list of all properties used in the application"),
106 _("Optional: check if system products are installed"),
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."""
124 _("Internal use: do not print labels, Works only with --value and --list."),
131 _("Internal use: print only keys, works only with --value."),
133 parser.add_option("s", "schema", "boolean", "schema", _("Internal use."))
138 shortcut wrapper to os.path.join
139 plus optionaly print for debug
141 res = os.path.realpath(os.path.join(*args))
143 if True: # ".pyconf" in res:
144 logger.info("osJoin %-80s in %s" % (res, CALN.caller_name(1)))
149 """Class that helps to find an application pyconf
150 in all the possible directories (pathList)
153 def __init__(self, pathList):
156 :param pathList list: The list of paths where to search a pyconf.
158 self.pathList = pathList
160 for path in pathList:
161 if not os.path.isdir(path):
162 logger.warning("ConfigOpener inexisting directory: %s" % path)
164 def __call__(self, name):
165 if os.path.isabs(name):
166 return src.pyconf.ConfigInputStream(open(name, "rb"))
168 return src.pyconf.ConfigInputStream(
169 open(osJoin(self.get_path(name), name), "rb")
171 raise IOError(_("Configuration file '%s' not found") % name)
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
177 :param name str: The name of the searched pyconf.
179 for path in self.pathList:
180 if os.path.exists(osJoin(path, name)):
182 raise IOError(_("Configuration file '%s' not found") % name)
186 """Class that manages the read of all the configuration files of salomeTools"""
188 def __init__(self, datadir=None):
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...
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
199 :return: The dictionary that stores all information.
203 var["user"] = src.architecture.get_user()
204 var["salometoolsway"] = os.path.dirname(
205 os.path.dirname(os.path.abspath(__file__))
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"
213 var["scriptExtension"] = ".sh"
215 # datadir has a default location
216 var["datadir"] = osJoin(var["salometoolsway"], "data")
217 if datadir is not None:
218 var["datadir"] = datadir
220 var["personalDir"] = osJoin(os.path.expanduser("~"), ".salomeTools")
221 src.ensure_path_exists(var["personalDir"])
223 var["personal_applications_dir"] = osJoin(var["personalDir"], "Applications")
224 src.ensure_path_exists(var["personal_applications_dir"])
226 var["personal_products_dir"] = osJoin(var["personalDir"], "products")
227 src.ensure_path_exists(var["personal_products_dir"])
229 var["personal_archives_dir"] = osJoin(var["personalDir"], "Archives")
230 src.ensure_path_exists(var["personal_archives_dir"])
232 var["personal_jobs_dir"] = osJoin(var["personalDir"], "Jobs")
233 src.ensure_path_exists(var["personal_jobs_dir"])
235 var["personal_machines_dir"] = osJoin(var["personalDir"], "Machines")
236 src.ensure_path_exists(var["personal_machines_dir"])
238 # read linux distributions dictionary
239 distrib_cfg = src.pyconf.Config(
240 osJoin(var["srcDir"], "internal_config", "distrib.pyconf")
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
249 var["dist_name"] = dist_name
250 var["dist_version"] = dist_version
252 var["dist_ref"] = dist_name + dist_version_full
253 var["python"] = src.architecture.get_python_version()
255 var["nb_proc"] = src.architecture.get_nb_proc()
256 node_name = platform.node()
257 var["node"] = node_name
258 var["hostname"] = node_name
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")
266 var["command"] = str(command)
267 var["application"] = str(application)
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"
277 def get_command_line_overrides(self, options, sections):
278 """get all the overwrites that are in the command line
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.
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:
292 for section in sections:
293 # only overwrite the sections that correspond to the option
295 filter(lambda l: l.startswith(section + "."), options.overwrite)
299 def get_config(self, application=None, options=None, command=None, datadir=None):
300 """get the config from all the configuration files.
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'
312 # create a ConfigMerger to handle merge
313 merger = src.pyconf.ConfigMerger() # MergeHandler())
315 # create the configuration instance
316 cfg = src.pyconf.Config()
318 # =====================================================================
319 # create VARS section
320 var = self._create_vars(
321 application=application, command=command, datadir=datadir
323 # DBG.write("create_vars", var, DBG.isDeveloper())
326 cfg.VARS = src.pyconf.Mapping(cfg)
328 cfg.VARS[variable] = var[variable]
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
334 # =====================================================================
335 # Load INTERNAL config
336 # read src/internal_config/salomeTools.pyconf
337 src.pyconf.streamOpener = ConfigOpener(
338 [osJoin(cfg.VARS.srcDir, "internal_config")]
341 if src.architecture.is_windows(): # special internal config for windows
342 internal_cfg = src.pyconf.Config(
345 cfg.VARS.srcDir, "internal_config", "salomeTools_win.pyconf"
350 internal_cfg = src.pyconf.Config(
352 osJoin(cfg.VARS.srcDir, "internal_config", "salomeTools.pyconf")
355 except src.pyconf.ConfigError as e:
356 raise src.SatException(
357 _("Error in configuration file:" " salomeTools.pyconf\n %(error)s")
361 merger.merge(cfg, internal_cfg)
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
367 # =====================================================================
368 # Load LOCAL config file
369 # search only in the data directory
370 src.pyconf.streamOpener = ConfigOpener([cfg.VARS.datadir])
372 local_cfg = src.pyconf.Config(
373 open(osJoin(cfg.VARS.datadir, "local.pyconf")),
374 PWD=("LOCAL", cfg.VARS.datadir),
376 except src.pyconf.ConfigError as e:
377 raise src.SatException(
378 _("Error in configuration file: " "local.pyconf\n %(error)s")
381 except IOError as error:
383 raise src.SatException(e)
384 merger.merge(cfg, local_cfg)
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")
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")
398 if cfg.LOCAL.archive_dir == "default":
399 cfg.LOCAL.archive_dir = os.path.abspath(
400 osJoin(cfg.VARS.salometoolsway, "..", "ARCHIVES")
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
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
415 # =====================================================================
417 projects_cfg = src.pyconf.Config()
418 projects_cfg.addMapping(
419 "PROJECTS", src.pyconf.Mapping(projects_cfg), "The projects\n"
421 projects_cfg.PROJECTS.addMapping(
422 "projects", src.pyconf.Mapping(cfg.PROJECTS), "The projects definition\n"
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
431 if not os.path.exists(project_pyconf_path):
433 "WARNING: The project file %s cannot be found. "
434 "It will be ignored\n" % project_pyconf_path
436 sys.stdout.write(msg)
438 project_name = os.path.basename(project_pyconf_path)[: -len(".pyconf")]
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)
444 except Exception as e:
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)
451 projects_cfg.PROJECTS.projects.addMapping(
453 src.pyconf.Mapping(projects_cfg.PROJECTS.projects),
454 "The %s project\n" % project_name,
456 projects_cfg.PROJECTS.projects[project_name] = project_cfg
457 projects_cfg.PROJECTS.projects[project_name][
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)
464 if product_project_git_tag:
465 projects_cfg.PROJECTS.projects[project_name][
467 ] = product_project_git_tag
469 projects_cfg.PROJECTS.projects[project_name]["git_tag"] = "unknown"
471 merger.merge(cfg, projects_cfg)
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
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, "")
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)
496 # initialise the path with local directory
497 cfg.PATHS["ARCHIVEPATH"].append(cfg.LOCAL.archive_dir, "")
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:
505 "ARCHIVEPATH", # comment this for default archive #8646
511 if PATH not in cfg.PROJECTS.projects[project]:
513 pathlist = cfg.PROJECTS.projects[project][PATH].split(":")
514 for path in pathlist:
515 cfg.PATHS[PATH].append(path, "")
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
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")
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)
534 application_cfg = src.pyconf.Config(application + ".pyconf")
536 raise src.SatException(
538 "%s, use 'config --list' to get the list of available applications."
542 except src.pyconf.ConfigError as e:
544 not ("-e" in parser.parse_args()[1])
545 or ("--edit" in parser.parse_args()[1])
546 and command == "config"
548 raise src.SatException(
550 "Error in configuration file: "
551 "%(application)s.pyconf\n "
554 % {"application": application, "error": str(e)}
558 src.printcolors.printcWarning(
559 "There is an error in the file"
560 " %s.pyconf.\n" % cfg.VARS.application
564 except Exception as e:
566 not ("-e" in parser.parse_args()[1])
567 or ("--edit" in parser.parse_args()[1])
568 and command == "config"
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}
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
584 "The error:" " %s\n" % src.printcolors.printcWarning(str(e))
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"
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
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
613 prod_cfg = src.pyconf.Config(
614 open(product_file_path), PWD=("", products_dir)
616 prod_cfg.from_file = product_file_path
617 products_cfg.PRODUCTS[product_name] = prod_cfg
618 except Exception as e:
620 "WARNING: Error in configuration file"
621 ": %(prod)s\n %(error)s"
622 % {"prod": product_name, "error": str(e)}
624 sys.stdout.write(msg)
626 merger.merge(cfg, products_cfg)
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
633 merger.merge(cfg, application_cfg)
635 # default launcher name ('salome')
637 "profile" in cfg.APPLICATION
638 and "launcher_name" not in cfg.APPLICATION.profile
640 cfg.APPLICATION.profile.launcher_name = "salome"
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
647 # =====================================================================
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)
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
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")
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.
670 :param config class 'src.pyconf.Config': The global config
671 (containing all pyconf).
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
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)
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.
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'
692 cfg_name = self.get_user_config_file()
694 user_cfg = src.pyconf.Config()
696 user_cfg.addMapping("USER", src.pyconf.Mapping(user_cfg), "")
698 user_cfg.USER.addMapping(
701 "This is the user name used to access salome cvs base.\n",
703 user_cfg.USER.addMapping(
706 "This is the user name used to access salome svn base.\n",
708 user_cfg.USER.addMapping(
709 "output_verbose_level",
711 "This is the default output_verbose_level you want."
712 " 0=>no output, 5=>debug.\n",
714 user_cfg.USER.addMapping(
716 osJoin(os.path.expanduser("~"), "websupport", "satreport"),
719 user_cfg.USER.addMapping(
720 "editor", "vi", "This is the editor used to " "modify configuration files\n"
722 user_cfg.USER.addMapping(
725 "This is the browser used to " "read html documentation\n",
727 user_cfg.USER.addMapping(
730 "This is the pdf_viewer used " "to read pdf documentation\n",
733 src.ensure_path_exists(config.VARS.personalDir)
734 src.ensure_path_exists(osJoin(config.VARS.personalDir, "Applications"))
736 f = open(cfg_name, "w")
742 def get_user_config_file(self):
743 """Get the user config file
744 :return: path to the user config file.
747 if not self.user_config_file_path:
748 raise src.SatException(
749 _("Error in get_user_config_file: " "missing user config file path")
751 return self.user_config_file_path
754 def check_path(path, ext=[]):
755 """Construct a text with the input path and "not found" if it does not
758 :param path Str: the path to check.
759 :param ext List: An extension. Verify that the path extension
761 :return: The string of the path with information
764 # check if file exists
765 if not os.path.exists(path):
766 return "'%s'" % path + " " + src.printcolors.printcError(_("** not found"))
770 fe = os.path.splitext(path)[1].lower()
773 "'%s'" % path + " " + src.printcolors.printcError(_("** bad extension"))
779 def show_product_info(config, name, logger):
780 """Display on the terminal and logger information about a product.
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
787 logger.write(_("%s is a product\n") % src.printcolors.printcLabel(name), 2)
788 pinfo = src.product.get_product_config(config, name)
790 if "depend" in pinfo:
791 src.printcolors.print_value(logger, "depends on", sorted(pinfo.depend), 2)
793 if "opt_depend" in pinfo:
794 src.printcolors.print_value(logger, "optional", sorted(pinfo.opt_depend), 2)
796 if "build_depend" in pinfo:
797 src.printcolors.print_value(
798 logger, "build depend on", sorted(pinfo.build_depend), 2
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)
809 # information on prepare
810 logger.write("\n", 2)
811 logger.write(src.printcolors.printcLabel("prepare:") + "\n", 2)
813 is_dev = src.product.product_is_dev(pinfo)
814 method = pinfo.get_source
817 src.printcolors.print_value(logger, "get method", method, 2)
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
824 src.printcolors.print_value(logger, "source", pinfo.cvs_info.source, 2)
825 src.printcolors.print_value(logger, "tag", pinfo.cvs_info.tag, 2)
827 elif method == "svn":
828 src.printcolors.print_value(logger, "repo", pinfo.svn_info.repo, 2)
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)
834 elif method == "archive":
835 src.printcolors.print_value(
836 logger, "get from", check_path(pinfo.archive_info.archive_name), 2
839 if "patches" in pinfo:
840 for patch in pinfo.patches:
841 src.printcolors.print_value(logger, "patch", check_path(patch), 2)
843 if src.product.product_is_fixed(pinfo):
844 src.printcolors.print_value(
845 logger, "install_dir", check_path(pinfo.install_dir), 2
848 if src.product.product_is_native(pinfo) or src.product.product_is_fixed(pinfo):
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)
857 if pinfo.build_source == "script" and "compil_script" in pinfo:
858 src.printcolors.print_value(
859 logger, "Compilation script", pinfo.compil_script, 2
862 if "nb_proc" in pinfo:
863 src.printcolors.print_value(logger, "make -j", pinfo.nb_proc, 2)
865 src.printcolors.print_value(
866 logger, "source dir", check_path(pinfo.source_dir), 2
868 if "install_dir" in pinfo:
869 src.printcolors.print_value(
870 logger, "build dir", check_path(pinfo.build_dir), 2
872 src.printcolors.print_value(
873 logger, "install dir", check_path(pinfo.install_dir), 2
877 " " + src.printcolors.printcWarning(_("no install dir")) + "\n", 2
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)
886 logger.write("\n", 2)
887 msg = _("This product does not compile")
888 logger.write("%s\n" % msg, 2)
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
898 # display run-time environment
899 zz = src.environment.SalomeEnviron(
900 config, src.fileEnviron.ScreenEnviron(logger), False
902 zz.set_python_libdirs()
903 zz.set_a_product(name, logger)
904 logger.write("\n", 2)
907 def show_patchs(config, logger):
908 """Prints all the used patchs in the application.
910 :param config Config: the global configuration.
911 :param logger Logger: The logger instance to use for the display
914 for product in sorted(config.APPLICATION.products):
916 product_info = src.product.get_product_config(config, product)
917 if src.product.product_has_patches(product_info):
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))
927 logger.write("\n", 1)
929 logger.write("No patchs found\n", 1)
932 def check_install_system(config, logger):
933 """Check the installation of all (declared) system products
935 :param config Config: the global configuration.
936 :param logger Logger: The logger instance to use for the display
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):
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
954 # logger.write("\n*** %s ***\n" % product, 1)
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)
965 except Exception as e:
966 msg = "\nproblem with the check of system prerequisite %s\n%s\n" % (
973 logger.write("\n\n", 1)
976 "Some run time system dependencies are missing!\n"
977 + "Please install them with %s before running salome" % pkgmgr
980 logger.write("missing run time dependencies : ", 1)
981 for md in run_dep_ko:
982 logger.write(md + " ", 1)
987 "Some compile time system dependencies are missing!\n"
988 + "Please install them with %s before compiling salome" % pkgmgr
991 logger.write("missing compile time dependencies : ", 1)
992 for md in build_dep_ko:
993 logger.write(md + " ", 1)
997 def show_dependencies(config, products, logger):
998 """Prints dependencies of products in the application.
1000 :param config Config: the global configuration.
1001 :param logger Logger: The logger instance to use for the display
1004 from compile import get_dependencies_graph, depth_search_graph, find_path_graph
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
1010 all_products_graph = get_dependencies_graph(all_products_infos, compile_time=False)
1013 product_liste_name = ""
1014 if products is None:
1015 products_list = config.APPLICATION.products
1016 products_graph = all_products_graph
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)
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)
1027 # 2. Extend the list with all the dependencies of the given list of products
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
1041 logger.write("Dependency graph (python format)\n%s\n" % products_graph, 3)
1043 gv_file_name = "%s_%s_dep.gv" % (config.VARS.application, product_liste_name)
1045 "\nDependency graph (graphviz format) written in file %s\n"
1046 % src.printcolors.printcLabel(gv_file_name),
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))
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,
1063 "\nReverse dependency graph (graphviz format) written in file %s\n"
1064 % src.printcolors.printcLabel(gv_revfile_name),
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))
1074 graph_cmd = "dot -Tpdf %s -o %s.pdf" % (gv_file_name, gv_file_name)
1076 "\nTo generate a graph use dot tool : \n %s"
1077 % src.printcolors.printcLabel(graph_cmd),
1082 def show_install_dir(config, logger):
1083 """Prints all the used installed directories in the application.
1085 :param config Config: the global configuration.
1086 :param logger Logger: The logger instance to use for the display
1088 for product in sorted(config.APPLICATION.products):
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))
1100 logger.write("\n", 1)
1103 def show_properties(config, logger):
1104 """Prints all the used properties in the application.
1106 :param config Config: the global configuration.
1107 :param logger Logger: The logger instance to use for the display
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:
1114 src.printcolors.printcInfo(
1115 " %s : %s\n" % (prop, config.APPLICATION.properties[prop])
1120 for product in sorted(config.APPLICATION.products):
1122 product_info = src.product.get_product_config(config, product)
1125 for prop in product_info.properties:
1127 logger.write("%s:\n" % product, 1)
1131 src.printcolors.printcInfo(
1132 " %s : %s\n" % (prop, product_info.properties[prop])
1136 except Exception as e:
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)
1144 logger.write("\n", 1)
1146 logger.write("No properties found\n", 1)
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.
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 :
1163 # Make sure that the path does not ends with a point
1164 if path.endswith("."):
1167 # display all the path or not
1171 vname = path.split(".")[-1]
1173 # number of spaces before the display
1174 tab_level = " " * level
1176 # call to the function that gets the value of the path.
1178 val = config.getByPath(path)
1179 except Exception as e:
1180 logger.write(tab_level)
1183 % (src.printcolors.printcLabel(vname), src.printcolors.printcError(str(e)))
1187 # in this case, display only the value
1189 logger.write(tab_level)
1190 logger.write("%s: " % src.printcolors.printcLabel(vname))
1192 # The case where the value has under values,
1193 # do a recursive call to the function
1194 if dir(val).__contains__("keys"):
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)
1206 config, path + "[" + str(index) + "]", show_label, logger, level + 1
1209 else: # case where val is just a str
1210 logger.write("%s\n" % val)
1213 def get_config_children(config, args):
1214 """Gets the names of the children of the given parameter.
1215 Useful only for completion mechanism
1217 :param config Config: The configuration where to read the values
1218 :param args: The path in the config from which get the keys
1221 rootkeys = config.keys()
1224 # no parameter returns list of root keys
1228 pos = parent.rfind(".")
1230 # Case where there is only on key as parameter.
1232 vals = [m for m in rootkeys if m.startswith(parent)]
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 :]
1239 a = config.getByPath(head)
1240 if dir(a).__contains__("keys"):
1242 lambda x: head + "." + x,
1243 [m for m in a.keys() if m.startswith(tail)],
1248 for v in sorted(vals):
1249 sys.stdout.write("%s\n" % v)
1253 """method that is called when salomeTools is called with --help option.
1255 :return: The text to display for the config command description.
1259 "The config command allows manipulation "
1260 "and operation on config files.\n\nexample:\nsat config "
1261 "SALOME-master --info ParaView"
1265 def run(args, runner, logger):
1266 """method that is called when salomeTools is called with config parameter."""
1268 (options, args) = parser.parse_args(args)
1270 # Only useful for completion mechanism : print the keys of the config
1272 get_config_children(runner.cfg, args)
1275 # case : print a value of the config
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)
1285 not options.no_label,
1288 show_full_path=False,
1291 # case : print a debug value of the config
1293 if options.debug == ".":
1294 # if argument is ".", print all the config
1295 res = DBG.indent(DBG.getStrConfigDbg(runner.cfg))
1297 "\nConfig of application %s:\n\n%s\n"
1298 % (runner.cfg.VARS.application, res)
1301 if options.debug[0] == ".": # accept ".PRODUCT.etc" as "PRODUCT.etc"
1302 od = options.debug[1:]
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"]))
1314 "\nConfig.%s of application %s:\n\n%s\n"
1315 % (od, runner.cfg.VARS.application, res)
1317 except Exception as e:
1318 msg = "\nConfig.%s of application %s: Unknown pyconf key\n" % (
1320 runner.cfg.VARS.application,
1322 logger.write(src.printcolors.printcError(msg), 1)
1324 # case : edit user pyconf file or application file
1326 editor = runner.cfg.USER.editor
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)
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)
1342 # case : give information about the product(s) in parameter
1343 if options.products:
1344 if options.info is not None:
1346 "options.products %s overrides options.info %s"
1347 % (options.products, options.info)
1349 options.info = options.products
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:
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))
1367 msg = _("%s is not a product of %s.\n") % (
1369 runner.cfg.VARS.application,
1372 # raise Exception(msg)
1374 # case : copy an existing <application>.pyconf
1375 # to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
1377 # product is required
1378 src.check_config_has_application(runner.cfg)
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:
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
1393 if len(source_full_path) == 0:
1394 raise src.SatException(_("Config file for product %s not found\n") % source)
1397 # a name is given as parameter, use it
1399 elif "copy_prefix" in runner.cfg.INTERNAL.config:
1402 runner.cfg.INTERNAL.config.copy_prefix + runner.cfg.VARS.application
1405 # use same name as source
1406 dest = runner.cfg.VARS.application
1410 runner.cfg.VARS.personalDir, "Applications", dest + ".pyconf"
1412 if os.path.exists(dest_file):
1413 raise src.SatException(
1414 _("A personal application" " '%s' already exists") % dest
1418 shutil.copyfile(source_full_path, dest_file)
1419 logger.write(_("%s has been created.\n") % dest_file)
1421 # case : display all the available pyconf applications
1424 # search in all directories that can have pyconf applications
1425 for path in runner.cfg.PATHS.APPLICATIONPATH:
1427 if not options.no_label:
1428 logger.write("------ %s\n" % src.printcolors.printcHeader(path))
1430 if not os.path.exists(path):
1432 src.printcolors.printcError(_("Directory not found")) + "\n"
1435 for f in sorted(os.listdir(path)):
1436 # ignore file that does not ends with .pyconf
1437 if not f.endswith(".pyconf"):
1440 appliname = f[: -len(".pyconf")]
1441 if appliname not in lproduct:
1442 lproduct.append(appliname)
1444 path.startswith(runner.cfg.VARS.personalDir)
1445 and not options.no_label
1447 logger.write("%s*\n" % appliname)
1449 logger.write("%s\n" % appliname)
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)
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
1463 _("Patchs of application %s\n")
1464 % src.printcolors.printcLabel(runner.cfg.VARS.application),
1467 logger.write("\n", 2, False)
1468 show_patchs(runner.cfg, logger)
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
1475 _("Installation directories of application %s\n")
1476 % src.printcolors.printcLabel(runner.cfg.VARS.application),
1479 logger.write("\n", 2, False)
1480 show_install_dir(runner.cfg, logger)
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
1488 "List of run-time dependencies of the application %s, product by product\n"
1490 % src.printcolors.printcLabel(runner.cfg.VARS.application),
1493 logger.write("\n", 2, False)
1494 show_dependencies(runner.cfg, options.products, logger)
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)
1500 # Print some informations
1502 _("Properties of application %s\n")
1503 % src.printcolors.printcLabel(runner.cfg.VARS.application),
1506 logger.write("\n", 2, False)
1507 show_properties(runner.cfg, logger)
1509 # check system prerequisites
1510 if options.check_system:
1511 check_install_system(runner.cfg, logger)