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
28 import src.logger as LOG
29 import src.debug as DBG
30 import src.callerName as CALN
32 logger = LOG.getDefaultLogger()
34 verbose = False # True for debug
36 # internationalization
37 satdir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
38 gettext.install('salomeTools', os.path.join(satdir, 'src', 'i18n'))
40 # Define all possible option for config command : sat config <options>
41 parser = src.options.Options()
42 parser.add_option('v', 'value', 'string', 'value',
43 _("Optional: print the value of CONFIG_VARIABLE."))
44 parser.add_option('g', 'debug', 'string', 'debug',
45 _("Optional: print the debugging mode value of CONFIG_VARIABLE."))
46 parser.add_option('e', 'edit', 'boolean', 'edit',
47 _("Optional: edit the product configuration file."))
48 parser.add_option('i', 'info', 'list2', 'info',
49 _("Optional: get information on product(s). This option accepts a comma separated list."))
50 parser.add_option('p', 'products', 'list2', 'products',
51 _("Optional: same as --info, for convenience."))
52 parser.add_option('l', 'list', 'boolean', 'list',
53 _("Optional: list all available applications."))
54 parser.add_option('', 'show_patchs', 'boolean', 'show_patchs',
55 _("Optional: synthetic list of all patches used in the application"))
56 parser.add_option('', 'show_dependencies', 'boolean', 'show_dependencies',
57 _("Optional: list of product dependencies in the application"))
58 parser.add_option('', 'show_install', 'boolean', 'show_install',
59 _("Optional: synthetic list of all install directories in the application"))
60 parser.add_option('', 'show_properties', 'boolean', 'show_properties',
61 _("Optional: synthetic list of all properties used in the application"))
62 parser.add_option('', 'check_system', 'boolean', 'check_system',
63 _("Optional: check if system products are installed"))
64 parser.add_option('c', 'copy', 'boolean', 'copy',
65 _("""Optional: copy a config file to the personal config files directory.
66 WARNING: the included files are not copied.
67 If a name is given the new config file takes the given name."""))
68 parser.add_option('n', 'no_label', 'boolean', 'no_label',
69 _("Internal use: do not print labels, Works only with --value and --list."))
70 parser.add_option('', 'completion', 'boolean', 'completion',
71 _("Internal use: print only keys, works only with --value."))
72 parser.add_option('s', 'schema', 'boolean', 'schema',
77 shortcut wrapper to os.path.join
78 plus optionaly print for debug
80 res = os.path.realpath(os.path.join(*args))
82 if True: # ".pyconf" in res:
83 logger.info("osJoin %-80s in %s" % (res, CALN.caller_name(1)))
87 '''Class that helps to find an application pyconf
88 in all the possible directories (pathList)
90 def __init__(self, pathList):
93 :param pathList list: The list of paths where to search a pyconf.
95 self.pathList = pathList
98 if not os.path.isdir(path):
99 logger.warning("ConfigOpener inexisting directory: %s" % path)
101 def __call__(self, name):
102 if os.path.isabs(name):
103 return src.pyconf.ConfigInputStream(open(name, 'rb'))
105 return src.pyconf.ConfigInputStream(open(osJoin(self.get_path(name), name), 'rb'))
106 raise IOError(_("Configuration file '%s' not found") % name)
108 def get_path( self, name ):
109 '''The method that returns the entire path of the pyconf searched
110 returns first found in self.pathList directories
112 :param name str: The name of the searched pyconf.
114 for path in self.pathList:
115 if os.path.exists(osJoin(path, name)):
117 raise IOError(_("Configuration file '%s' not found") % name)
120 '''Class that manages the read of all the configuration files of salomeTools
122 def __init__(self, datadir=None):
125 def _create_vars(self, application=None, command=None, datadir=None):
126 '''Create a dictionary that stores all information about machine,
127 user, date, repositories, etc...
129 :param application str: The application for which salomeTools is called.
130 :param command str: The command that is called.
131 :param datadir str: The repository that contain external data
133 :return: The dictionary that stores all information.
137 var['user'] = src.architecture.get_user()
138 var['salometoolsway'] = os.path.dirname( os.path.dirname(os.path.abspath(__file__)))
139 var['srcDir'] = osJoin(var['salometoolsway'], 'src')
140 var['internal_dir'] = osJoin(var['srcDir'], 'internal_config')
141 var['sep']= os.path.sep
142 if src.architecture.is_windows():
143 var['scriptExtension'] = '.bat'
145 var['scriptExtension'] = '.sh'
147 # datadir has a default location
148 var['datadir'] = osJoin(var['salometoolsway'], 'data')
149 if datadir is not None:
150 var['datadir'] = datadir
151 var['personalDir'] = osJoin(os.path.expanduser('~'), '.salomeTools')
152 src.ensure_path_exists(var['personalDir'])
154 var['personal_applications_dir'] = osJoin(var['personalDir'], "Applications")
155 src.ensure_path_exists(var['personal_applications_dir'])
157 var['personal_products_dir'] = osJoin(var['personalDir'], "products")
158 src.ensure_path_exists(var['personal_products_dir'])
160 var['personal_archives_dir'] = osJoin(var['personalDir'], "Archives")
161 src.ensure_path_exists(var['personal_archives_dir'])
163 var['personal_jobs_dir'] = osJoin(var['personalDir'], "Jobs")
164 src.ensure_path_exists(var['personal_jobs_dir'])
166 var['personal_machines_dir'] = osJoin(var['personalDir'], "Machines")
167 src.ensure_path_exists(var['personal_machines_dir'])
169 # read linux distributions dictionary
170 distrib_cfg = src.pyconf.Config( osJoin(var['srcDir'], 'internal_config', 'distrib.pyconf'))
172 # set platform parameters
173 dist_name = src.architecture.get_distribution(codes=distrib_cfg.DISTRIBUTIONS)
174 dist_version = src.architecture.get_distrib_version(dist_name)
175 dist_version_full = src.architecture.get_version_XY()
176 dist = dist_name + dist_version
178 var['dist_name'] = dist_name
179 var['dist_version'] = dist_version
181 var['dist_ref'] = dist_name + dist_version_full
182 var['python'] = src.architecture.get_python_version()
184 var['nb_proc'] = src.architecture.get_nb_proc()
185 node_name = platform.node()
186 var['node'] = node_name
187 var['hostname'] = node_name
189 # set date parameters
190 dt = datetime.datetime.now()
191 var['date'] = dt.strftime('%Y%m%d')
192 var['datehour'] = dt.strftime('%Y%m%d_%H%M%S')
193 var['hour'] = dt.strftime('%H%M%S')
195 var['command'] = str(command)
196 var['application'] = str(application)
198 # Root dir for temporary files
199 var['tmp_root'] = os.sep + 'tmp' + os.sep + var['user']
200 # particular win case
201 if src.architecture.is_windows() :
202 var['tmp_root'] = os.path.expanduser('~') + os.sep + 'tmp'
206 def get_command_line_overrides(self, options, sections):
207 '''get all the overwrites that are in the command line
209 :param options: the options from salomeTools class
210 initialization (like -l5 or --overwrite)
211 :param sections str: The config section to overwrite.
212 :return: The list of all the overwrites to apply.
215 # when there are no options or not the overwrite option,
216 # return an empty list
217 if options is None or options.overwrite is None:
221 for section in sections:
222 # only overwrite the sections that correspond to the option
223 over.extend(filter(lambda l: l.startswith(section + "."),
227 def get_config(self, application=None, options=None, command=None,
229 '''get the config from all the configuration files.
231 :param application str: The application for which salomeTools is called.
232 :param options class Options: The general salomeToos
233 options (--overwrite or -l5, for example)
234 :param command str: The command that is called.
235 :param datadir str: The repository that contain
236 external data for salomeTools.
237 :return: The final config.
238 :rtype: class 'src.pyconf.Config'
241 # create a ConfigMerger to handle merge
242 merger = src.pyconf.ConfigMerger()#MergeHandler())
244 # create the configuration instance
245 cfg = src.pyconf.Config()
247 # =====================================================================
248 # create VARS section
249 var = self._create_vars(application=application, command=command, datadir=datadir)
250 # DBG.write("create_vars", var, DBG.isDeveloper())
253 cfg.VARS = src.pyconf.Mapping(cfg)
255 cfg.VARS[variable] = var[variable]
257 # apply overwrite from command line if needed
258 for rule in self.get_command_line_overrides(options, ["VARS"]):
259 exec('cfg.' + rule) # this cannot be factorized because of the exec
261 # =====================================================================
262 # Load INTERNAL config
263 # read src/internal_config/salomeTools.pyconf
264 src.pyconf.streamOpener = ConfigOpener([
265 osJoin(cfg.VARS.srcDir, 'internal_config')])
267 if src.architecture.is_windows(): # special internal config for windows
268 internal_cfg = src.pyconf.Config(open( osJoin(cfg.VARS.srcDir,
269 'internal_config', 'salomeTools_win.pyconf')))
271 internal_cfg = src.pyconf.Config(open( osJoin(cfg.VARS.srcDir,
272 'internal_config', 'salomeTools.pyconf')))
273 except src.pyconf.ConfigError as e:
274 raise src.SatException(_("Error in configuration file:"
275 " salomeTools.pyconf\n %(error)s") % \
278 merger.merge(cfg, internal_cfg)
280 # apply overwrite from command line if needed
281 for rule in self.get_command_line_overrides(options, ["INTERNAL"]):
282 exec('cfg.' + rule) # this cannot be factorized because of the exec
284 # =====================================================================
285 # Load LOCAL config file
286 # search only in the data directory
287 src.pyconf.streamOpener = ConfigOpener([cfg.VARS.datadir])
289 local_cfg = src.pyconf.Config(open( osJoin(cfg.VARS.datadir,
291 PWD = ('LOCAL', cfg.VARS.datadir) )
292 except src.pyconf.ConfigError as e:
293 raise src.SatException(_("Error in configuration file: "
294 "local.pyconf\n %(error)s") % \
296 except IOError as error:
298 raise src.SatException( e );
299 merger.merge(cfg, local_cfg)
301 # When the key is "default", put the default value
302 if cfg.LOCAL.base == "default":
303 cfg.LOCAL.base = os.path.abspath(osJoin(cfg.VARS.salometoolsway, "..", "BASE"))
304 if cfg.LOCAL.workdir == "default":
305 cfg.LOCAL.workdir = os.path.abspath(osJoin(cfg.VARS.salometoolsway, ".."))
306 if cfg.LOCAL.log_dir == "default":
307 cfg.LOCAL.log_dir = os.path.abspath(osJoin(cfg.VARS.salometoolsway, "..", "LOGS"))
309 if cfg.LOCAL.archive_dir == "default":
310 cfg.LOCAL.archive_dir = os.path.abspath( osJoin(cfg.VARS.salometoolsway, "..", "ARCHIVES"))
312 # if the sat tag was not set permanently by user
313 if cfg.LOCAL.tag == "unknown":
314 # get the tag with git, and store it
315 sat_version=src.system.git_describe(cfg.VARS.salometoolsway)
316 if sat_version == False:
317 sat_version=cfg.INTERNAL.sat_version
318 cfg.LOCAL.tag=sat_version
321 # apply overwrite from command line if needed
322 for rule in self.get_command_line_overrides(options, ["LOCAL"]):
323 exec('cfg.' + rule) # this cannot be factorized because of the exec
325 # =====================================================================
327 projects_cfg = src.pyconf.Config()
328 projects_cfg.addMapping("PROJECTS",
329 src.pyconf.Mapping(projects_cfg),
331 projects_cfg.PROJECTS.addMapping("projects",
332 src.pyconf.Mapping(cfg.PROJECTS),
333 "The projects definition\n")
335 for project_pyconf_path in cfg.PROJECTS.project_file_paths:
336 if not os.path.isabs(project_pyconf_path):
337 # for a relative path (archive case) we complete with sat path
338 project_pyconf_path = os.path.join(cfg.VARS.salometoolsway,
340 if not os.path.exists(project_pyconf_path):
341 msg = _("WARNING: The project file %s cannot be found. "
342 "It will be ignored\n" % project_pyconf_path)
343 sys.stdout.write(msg)
345 project_name = os.path.basename(
346 project_pyconf_path)[:-len(".pyconf")]
348 project_pyconf_dir = os.path.dirname(project_pyconf_path)
349 project_cfg = src.pyconf.Config(open(project_pyconf_path),
350 PWD=("", project_pyconf_dir))
351 except Exception as e:
352 msg = _("ERROR: Error in configuration file: "
353 "%(file_path)s\n %(error)s\n") % \
354 {'file_path' : project_pyconf_path, 'error': str(e) }
355 sys.stdout.write(msg)
357 projects_cfg.PROJECTS.projects.addMapping(project_name,
358 src.pyconf.Mapping(projects_cfg.PROJECTS.projects),
359 "The %s project\n" % project_name)
360 projects_cfg.PROJECTS.projects[project_name]=project_cfg
361 projects_cfg.PROJECTS.projects[project_name]["file_path"] = \
363 # store the project tag if any
364 product_project_git_tag = src.system.git_describe(os.path.dirname(project_pyconf_path))
365 if product_project_git_tag:
366 projects_cfg.PROJECTS.projects[project_name]["git_tag"] = product_project_git_tag
368 projects_cfg.PROJECTS.projects[project_name]["git_tag"] = "unknown"
370 merger.merge(cfg, projects_cfg)
372 # apply overwrite from command line if needed
373 for rule in self.get_command_line_overrides(options, ["PROJECTS"]):
374 exec('cfg.' + rule) # this cannot be factorized because of the exec
376 # =====================================================================
377 # Create the paths where to search the application configurations,
378 # the product configurations, the products archives,
379 # the jobs configurations and the machines configurations
380 cfg.addMapping("PATHS", src.pyconf.Mapping(cfg), "The paths\n")
381 cfg.PATHS["APPLICATIONPATH"] = src.pyconf.Sequence(cfg.PATHS)
382 cfg.PATHS.APPLICATIONPATH.append(cfg.VARS.personal_applications_dir, "")
384 cfg.PATHS["PRODUCTPATH"] = src.pyconf.Sequence(cfg.PATHS)
385 cfg.PATHS.PRODUCTPATH.append(cfg.VARS.personal_products_dir, "")
386 cfg.PATHS["ARCHIVEPATH"] = src.pyconf.Sequence(cfg.PATHS)
387 cfg.PATHS.ARCHIVEPATH.append(cfg.VARS.personal_archives_dir, "")
388 cfg.PATHS["ARCHIVEFTP"] = src.pyconf.Sequence(cfg.PATHS)
389 cfg.PATHS["JOBPATH"] = src.pyconf.Sequence(cfg.PATHS)
390 cfg.PATHS.JOBPATH.append(cfg.VARS.personal_jobs_dir, "")
391 cfg.PATHS["MACHINEPATH"] = src.pyconf.Sequence(cfg.PATHS)
392 cfg.PATHS.MACHINEPATH.append(cfg.VARS.personal_machines_dir, "")
393 cfg.PATHS["LICENCEPATH"] = src.pyconf.Sequence(cfg.PATHS)
395 # initialise the path with local directory
396 cfg.PATHS["ARCHIVEPATH"].append(cfg.LOCAL.archive_dir, "")
398 # Loop over the projects in order to complete the PATHS variables
399 # as /data/tmpsalome/salome/prerequis/archives for example ARCHIVEPATH
400 for project in cfg.PROJECTS.projects:
401 for PATH in ["APPLICATIONPATH",
403 "ARCHIVEPATH", #comment this for default archive #8646
408 if PATH not in cfg.PROJECTS.projects[project]:
410 pathlist=cfg.PROJECTS.projects[project][PATH].split(":")
411 for path in pathlist:
412 cfg.PATHS[PATH].append(path, "")
414 # apply overwrite from command line if needed
415 for rule in self.get_command_line_overrides(options, ["PATHS"]):
416 exec('cfg.' + rule) # this cannot be factorized because of the exec
418 # add git servers if any
419 cfg.addMapping("git_info", src.pyconf.Mapping(cfg), "The repositories\n")
420 cfg.VARS['git_servers'] = []
421 cfg.VARS['opensource_git_servers'] =[]
423 for project in cfg.PROJECTS.projects:
424 if 'git_info' not in cfg.PROJECTS.projects[project]:
425 logger.warning("Project: {} does not have any git_info section! Please define one!")
427 if 'git_server' in cfg.PROJECTS.projects[project]['git_info']:
428 git_servers=cfg.PROJECTS.projects[project]['git_info']['git_server']
429 for git_server in git_servers:
430 cfg.VARS['git_servers']+=[git_server]
431 if git_servers[git_server]['opensource_only'] == 'yes' :
432 cfg.VARS['opensource_git_servers']+=[git_server]
433 if 'default_git_server_dev' in cfg.PROJECTS.projects[project]['git_info'].keys():
434 cfg.VARS['git_servers']+=['tuleap']
435 cfg.VARS['default_git_server_dev'] = cfg.PROJECTS.projects[project]['git_info']['default_git_server_dev']
436 if 'default_git_server' in cfg.PROJECTS.projects[project]['git_info'].keys():
437 cfg.VARS['git_servers']+=['gitpub']
438 cfg.VARS['opensource_git_servers']+=['gitpub']
439 cfg.VARS['default_git_server'] = cfg.PROJECTS.projects[project]['git_info']['default_git_server']
441 # AT END append APPLI_TEST directory in APPLICATIONPATH, for unittest
442 appli_test_dir = osJoin(satdir, "test", "APPLI_TEST")
443 if appli_test_dir not in cfg.PATHS.APPLICATIONPATH:
444 cfg.PATHS.APPLICATIONPATH.append(appli_test_dir, "unittest APPLI_TEST path")
445 # =====================================================================
446 # Load APPLICATION config file
447 if application is not None:
448 # search APPLICATION file in all directories in configPath
449 cp = cfg.PATHS.APPLICATIONPATH
450 src.pyconf.streamOpener = ConfigOpener(cp)
453 application_cfg = src.pyconf.Config(application + '.pyconf')
455 raise src.SatException(
456 _("%s, use 'config --list' to get the list of available applications.") % e)
457 except src.pyconf.ConfigError as e:
458 if (not ('-e' in parser.parse_args()[1])
459 or ('--edit' in parser.parse_args()[1])
460 and command == 'config'):
461 raise src.SatException(_("Error in configuration file: "
462 "%(application)s.pyconf\n "
464 { 'application': application, 'error': str(e) } )
466 sys.stdout.write(src.printcolors.printcWarning(
467 "There is an error in the file"
468 " %s.pyconf.\n" % cfg.VARS.application))
470 except Exception as e:
471 if (not ('-e' in parser.parse_args()[1])
472 or ('--edit' in parser.parse_args()[1])
473 and command == 'config'):
474 sys.stdout.write(src.printcolors.printcWarning("%s\n" % str(e)))
475 raise src.SatException(_("Error in configuration file:"
476 " %(application)s.pyconf\n") % \
477 { 'application': application} )
479 sys.stdout.write(src.printcolors.printcWarning(
480 "There is an error in the file"
481 " %s.pyconf. Opening the file with the"
482 " default viewer\n" % cfg.VARS.application))
483 sys.stdout.write("The error:"
484 " %s\n" % src.printcolors.printcWarning(
489 cfg['open_application'] = 'yes'
490 # =====================================================================
491 # Load product config files in PRODUCTS section
492 products_cfg = src.pyconf.Config()
493 products_cfg.addMapping("PRODUCTS",
494 src.pyconf.Mapping(products_cfg),
496 if application is not None:
497 src.pyconf.streamOpener = ConfigOpener(cfg.PATHS.PRODUCTPATH)
498 for product_name in application_cfg.APPLICATION.products.keys():
499 # Loop on all files that are in softsDir directory
500 # and read their config
501 product_file_name = product_name + ".pyconf"
502 product_file_path = src.find_file_in_lpath(product_file_name, cfg.PATHS.PRODUCTPATH)
503 if product_file_path:
504 products_dir = os.path.dirname(product_file_path)
505 # for a relative path (archive case) we complete with sat path
506 if not os.path.isabs(products_dir):
507 products_dir = os.path.join(cfg.VARS.salometoolsway,
510 prod_cfg = src.pyconf.Config(open(product_file_path),
511 PWD=("", products_dir))
512 prod_cfg.from_file = product_file_path
513 products_cfg.PRODUCTS[product_name] = prod_cfg
514 except Exception as e:
516 "WARNING: Error in configuration file"
517 ": %(prod)s\n %(error)s" % \
518 {'prod' : product_name, 'error': str(e) })
519 sys.stdout.write(msg)
521 merger.merge(cfg, products_cfg)
523 # apply overwrite from command line if needed
524 for rule in self.get_command_line_overrides(options, ["PRODUCTS"]):
525 exec('cfg.' + rule) # this cannot be factorized because of the exec
528 merger.merge(cfg, application_cfg)
530 # default launcher name ('salome')
531 if ('profile' in cfg.APPLICATION and
532 'launcher_name' not in cfg.APPLICATION.profile):
533 cfg.APPLICATION.profile.launcher_name = 'salome'
535 # apply overwrite from command line if needed
536 for rule in self.get_command_line_overrides(options,
538 # this cannot be factorized because of the exec
541 # =====================================================================
543 self.set_user_config_file(cfg)
544 user_cfg_file = self.get_user_config_file()
545 user_cfg = src.pyconf.Config(open(user_cfg_file))
546 merger.merge(cfg, user_cfg)
548 # apply overwrite from command line if needed
549 for rule in self.get_command_line_overrides(options, ["USER"]):
550 exec('cfg.' + rule) # this cannot be factorize because of the exec
552 # remove application products "blacklisted" in rm_products field
553 if "APPLICATION" in cfg and "rm_products" in cfg.APPLICATION:
554 for prod_to_remove in cfg.APPLICATION.rm_products:
555 cfg.APPLICATION.products.__delitem__(prod_to_remove)
556 # remove rm_products section after usage
557 cfg.APPLICATION.__delitem__("rm_products")
560 def set_user_config_file(self, config):
561 '''Set the user config file name and path.
562 If necessary, build it from another one or create it from scratch.
564 :param config class 'src.pyconf.Config': The global config
565 (containing all pyconf).
567 # get the expected name and path of the file
568 self.config_file_name = 'SAT.pyconf'
569 self.user_config_file_path = osJoin(config.VARS.personalDir, self.config_file_name)
571 # if pyconf does not exist, create it from scratch
572 if not os.path.isfile(self.user_config_file_path):
573 self.create_config_file(config)
575 def create_config_file(self, config):
576 '''This method is called when there are no user config file.
577 It build it from scratch.
579 :param config class 'src.pyconf.Config': The global config.
580 :return: the config corresponding to the file created.
581 :rtype: config class 'src.pyconf.Config'
584 cfg_name = self.get_user_config_file()
586 user_cfg = src.pyconf.Config()
588 user_cfg.addMapping('USER', src.pyconf.Mapping(user_cfg), "")
590 user_cfg.USER.addMapping('cvs_user', config.VARS.user,
591 "This is the user name used to access salome cvs base.\n")
592 user_cfg.USER.addMapping('svn_user', config.VARS.user,
593 "This is the user name used to access salome svn base.\n")
594 user_cfg.USER.addMapping('output_verbose_level', 3,
595 "This is the default output_verbose_level you want."
596 " 0=>no output, 5=>debug.\n")
597 user_cfg.USER.addMapping('publish_dir',
598 osJoin(os.path.expanduser('~'),
602 user_cfg.USER.addMapping('editor',
604 "This is the editor used to "
605 "modify configuration files\n")
606 user_cfg.USER.addMapping('browser',
608 "This is the browser used to "
609 "read html documentation\n")
610 user_cfg.USER.addMapping('pdf_viewer',
612 "This is the pdf_viewer used "
613 "to read pdf documentation\n")
615 src.ensure_path_exists(config.VARS.personalDir)
616 src.ensure_path_exists( osJoin(config.VARS.personalDir,
619 f = open(cfg_name, 'w')
625 def get_user_config_file(self):
626 '''Get the user config file
627 :return: path to the user config file.
630 if not self.user_config_file_path:
631 raise src.SatException(_("Error in get_user_config_file: "
632 "missing user config file path"))
633 return self.user_config_file_path
635 def check_path(path, ext=[]):
636 '''Construct a text with the input path and "not found" if it does not
639 :param path Str: the path to check.
640 :param ext List: An extension. Verify that the path extension
642 :return: The string of the path with information
645 # check if file exists
646 if not os.path.exists(path):
647 return "'%s'" % path + " " + src.printcolors.printcError(_(
652 fe = os.path.splitext(path)[1].lower()
654 return "'%s'" % path + " " + src.printcolors.printcError(_(
659 def show_product_info(config, name, logger):
660 '''Display on the terminal and logger information about a product.
662 :param config Config: the global configuration.
663 :param name Str: The name of the product
664 :param logger Logger: The logger instance to use for the display
667 logger.write(_("%s is a product\n") % src.printcolors.printcLabel(name), 2)
668 pinfo = src.product.get_product_config(config, name)
670 if "depend" in pinfo:
671 src.printcolors.print_value(logger, "depends on", sorted(pinfo.depend), 2)
673 if "opt_depend" in pinfo:
674 src.printcolors.print_value(logger, "optional", sorted(pinfo.opt_depend), 2)
676 if "build_depend" in pinfo:
677 src.printcolors.print_value(logger, "build depend on", sorted(pinfo.build_depend), 2)
680 # information on pyconf
681 logger.write("\n", 2)
682 logger.write(src.printcolors.printcLabel("configuration:") + "\n", 2)
683 if "from_file" in pinfo:
684 src.printcolors.print_value(logger,
688 if "section" in pinfo:
689 src.printcolors.print_value(logger,
694 # information on prepare
695 logger.write("\n", 2)
696 logger.write(src.printcolors.printcLabel("prepare:") + "\n", 2)
698 is_dev = src.product.product_is_dev(pinfo)
699 method = pinfo.get_source
702 src.printcolors.print_value(logger, "get method", method, 2)
705 src.printcolors.print_value(logger, "server", pinfo.cvs_info.server, 2)
706 src.printcolors.print_value(logger, "base module",
707 pinfo.cvs_info.module_base, 2)
708 src.printcolors.print_value(logger, "source", pinfo.cvs_info.source, 2)
709 src.printcolors.print_value(logger, "tag", pinfo.cvs_info.tag, 2)
711 elif method == 'svn':
712 src.printcolors.print_value(logger, "repo", pinfo.svn_info.repo, 2)
714 elif method == 'git':
715 src.printcolors.print_value(logger, "repo", pinfo.git_info.repo, 2)
716 src.printcolors.print_value(logger, "tag", pinfo.git_info.tag, 2)
718 elif method == 'archive':
719 src.printcolors.print_value(logger,
721 check_path(pinfo.archive_info.archive_name),
724 if 'patches' in pinfo:
725 for patch in pinfo.patches:
726 src.printcolors.print_value(logger, "patch", check_path(patch), 2)
728 if src.product.product_is_fixed(pinfo):
729 src.printcolors.print_value(logger, "install_dir",
730 check_path(pinfo.install_dir), 2)
732 if src.product.product_is_native(pinfo) or src.product.product_is_fixed(pinfo):
735 # information on compilation
736 if src.product.product_compiles(pinfo):
737 logger.write("\n", 2)
738 logger.write(src.printcolors.printcLabel("compile:") + "\n", 2)
739 src.printcolors.print_value(logger,
740 "compilation method",
744 if pinfo.build_source == "script" and "compil_script" in pinfo:
745 src.printcolors.print_value(logger,
746 "Compilation script",
750 if 'nb_proc' in pinfo:
751 src.printcolors.print_value(logger, "make -j", pinfo.nb_proc, 2)
753 src.printcolors.print_value(logger,
755 check_path(pinfo.source_dir),
757 if 'install_dir' in pinfo:
758 src.printcolors.print_value(logger,
760 check_path(pinfo.build_dir),
762 src.printcolors.print_value(logger,
764 check_path(pinfo.install_dir),
768 src.printcolors.printcWarning(_("no install dir")) +
771 src.printcolors.print_value(logger, "debug ", pinfo.debug, 2)
772 src.printcolors.print_value(logger, "verbose ", pinfo.verbose, 2)
773 src.printcolors.print_value(logger, "hpc ", pinfo.hpc, 2)
774 src.printcolors.print_value(logger, "dev ", pinfo.dev, 2)
777 logger.write("\n", 2)
778 msg = _("This product does not compile")
779 logger.write("%s\n" % msg, 2)
781 # information on environment
782 logger.write("\n", 2)
783 logger.write(src.printcolors.printcLabel("environ :") + "\n", 2)
784 if "environ" in pinfo and "env_script" in pinfo.environ:
785 src.printcolors.print_value(logger,
787 check_path(pinfo.environ.env_script),
790 # display run-time environment
791 zz = src.environment.SalomeEnviron(config,
792 src.fileEnviron.ScreenEnviron(logger),
794 zz.set_python_libdirs()
795 zz.set_a_product(name, logger)
796 logger.write("\n", 2)
799 def show_patchs(config, logger):
800 '''Prints all the used patchs in the application.
802 :param config Config: the global configuration.
803 :param logger Logger: The logger instance to use for the display
806 for product in sorted(config.APPLICATION.products):
808 product_info = src.product.get_product_config(config, product)
809 if src.product.product_has_patches(product_info):
811 logger.write("%s:\n" % product, 1)
812 for i in product_info.patches:
813 logger.write(src.printcolors.printcInfo(" %s\n" % i), 1)
814 except Exception as e:
815 msg = "problem on product %s\n%s\n" % (product, str(e))
819 logger.write("\n", 1)
821 logger.write("No patchs found\n", 1)
823 def check_install_system(config, logger):
824 '''Check the installation of all (declared) system products
826 :param config Config: the global configuration.
827 :param logger Logger: The logger instance to use for the display
829 # get the command to use for checking the system dependencies
830 # (either rmp or apt)
831 check_cmd=src.system.get_pkg_check_cmd(config.VARS.dist_name)
832 logger.write("\nCheck the system dependencies declared in the application\n",1)
834 run_dep_ko=[] # list of missing run time dependencies
835 build_dep_ko=[] # list of missing compile time dependencies
836 for product in sorted(config.APPLICATION.products):
838 product_info = src.product.get_product_config(config, product)
839 if src.product.product_is_native(product_info):
840 # if the product is native, get (in two dictionnaries the runtime and compile time
841 # system dependencies with the status (OK/KO)
842 run_pkg,build_pkg=src.product.check_system_dep(config.VARS.dist, check_cmd, product_info)
843 #logger.write("\n*** %s ***\n" % product, 1)
845 logger.write("\n - "+pkg + " : " + run_pkg[pkg], 1)
846 if "KO" in run_pkg[pkg]:
847 run_dep_ko.append(pkg)
848 for pkg in build_pkg:
849 logger.write("\n - "+pkg + " : " + build_pkg[pkg], 1)
850 if "KO" in build_pkg[pkg]:
851 build_dep_ko.append(pkg)
852 # logger.write(src.printcolors.printcInfo(" %s\n" % i), 1)
854 except Exception as e:
855 msg = "\nproblem with the check of system prerequisite %s\n%s\n" % (product, str(e))
859 logger.write("\n\n",1)
861 msg="Some run time system dependencies are missing!\n"+\
862 "Please install them with %s before running salome" % pkgmgr
864 logger.write("missing run time dependencies : ",1)
865 for md in run_dep_ko:
866 logger.write(md+" ",1)
870 msg="Some compile time system dependencies are missing!\n"+\
871 "Please install them with %s before compiling salome" % pkgmgr
873 logger.write("missing compile time dependencies : ",1)
874 for md in build_dep_ko:
875 logger.write(md+" ",1)
879 def show_dependencies(config, products, logger):
880 '''Prints dependencies of products in the application.
882 :param config Config: the global configuration.
883 :param logger Logger: The logger instance to use for the display
886 from compile import get_dependencies_graph,depth_search_graph,find_path_graph
887 # Get the list of all application products, and create its dependency graph
888 all_products_infos = src.product.get_products_infos(config.APPLICATION.products,config)
889 all_products_graph=get_dependencies_graph(all_products_infos, compile_time=False)
892 product_liste_name=""
894 products_list=config.APPLICATION.products
895 products_graph = all_products_graph
897 # 1. Extend the list with all products that depends upon the given list of products
898 products_list=products
899 product_liste_name="_".join(products)
901 for p_name in products_list:
902 visited=depth_search_graph(all_products_graph, p_name, visited)
903 products_infos = src.product.get_products_infos(visited, config)
904 products_graph = get_dependencies_graph(products_infos, compile_time=False)
906 # 2. Extend the list with all the dependencies of the given list of products
908 for n in all_products_graph:
909 # for all products (that are not in products_list):
910 # if we we find a path from the product to the product list,
911 # then we product is a child and we add it to the children list
912 if (n not in children) and (n not in products_list):
913 if find_path_graph(all_products_graph, n, products_list):
914 children = children + [n]
915 products_infos_rev = src.product.get_products_infos(children, config)
916 products_graph_rev = get_dependencies_graph(products_infos_rev, compile_time=False)
918 logger.write("Dependency graph (python format)\n%s\n" % products_graph, 3)
920 gv_file_name='%s_%s_dep.gv' % (config.VARS.application,product_liste_name)
921 logger.write("\nDependency graph (graphviz format) written in file %s\n" %
922 src.printcolors.printcLabel(gv_file_name), 3)
923 with open(gv_file_name,"w") as f:
924 f.write("digraph G {\n")
925 for p in products_graph:
926 for dep in products_graph[p]:
927 f.write ("\t%s -> %s\n" % (p,dep))
931 if products is not None:
932 # if a list of products was given, produce also the reverse dependencies
933 gv_revfile_name='%s_%s_rev_dep.gv' % (config.VARS.application,product_liste_name)
934 logger.write("\nReverse dependency graph (graphviz format) written in file %s\n" %
935 src.printcolors.printcLabel(gv_revfile_name), 3)
936 with open(gv_revfile_name,"w") as rf:
937 rf.write("digraph G {\n")
938 for p in products_graph_rev:
939 for dep in products_graph_rev[p]:
940 rf.write ("\t%s -> %s\n" % (p,dep))
943 graph_cmd = "dot -Tpdf %s -o %s.pdf" % (gv_file_name,gv_file_name)
944 logger.write("\nTo generate a graph use dot tool : \n %s" %
945 src.printcolors.printcLabel(graph_cmd), 3)
947 def show_install_dir(config, logger):
948 '''Prints all the used installed directories in the application.
950 :param config Config: the global configuration.
951 :param logger Logger: The logger instance to use for the display
953 for product in sorted(config.APPLICATION.products):
955 product_info = src.product.get_product_config(config, product)
956 install_path=src.Path(product_info.install_dir)
957 if (src.product.product_is_native(product_info)):
958 install_path="Native"
959 elif (src.product.product_is_fixed(product_info)):
960 install_path+=" (Fixed)"
961 logger.write("%s : %s\n" % (product, install_path) , 1)
962 except Exception as e:
963 msg = "problem on product %s\n%s\n" % (product, str(e))
965 logger.write("\n", 1)
968 def show_properties(config, logger):
969 '''Prints all the used properties in the application.
971 :param config Config: the global configuration.
972 :param logger Logger: The logger instance to use for the display
974 if "properties" in config.APPLICATION:
975 # some properties are defined at application level, we display them
976 logger.write("Application properties:\n", 1)
977 for prop in config.APPLICATION.properties:
978 logger.write(src.printcolors.printcInfo(" %s : %s\n" % (prop, config.APPLICATION.properties[prop])), 1)
980 for product in sorted(config.APPLICATION.products):
982 product_info = src.product.get_product_config(config, product)
985 for prop in product_info.properties:
987 logger.write("%s:\n" % product, 1)
990 logger.write(src.printcolors.printcInfo(" %s : %s\n" % (prop, product_info.properties[prop])), 1)
991 except Exception as e:
993 except Exception as e:
994 # logger.write(src.printcolors.printcInfo(" %s\n" % "no properties"), 1)
995 msg = "problem on product %s\n%s\n" % (product, e)
999 logger.write("\n", 1)
1001 logger.write("No properties found\n", 1)
1003 def print_value(config, path, show_label, logger, level=0, show_full_path=False):
1004 '''Prints a value from the configuration. Prints recursively the values
1005 under the initial path.
1007 :param config class 'src.pyconf.Config': The configuration
1008 from which the value is displayed.
1009 :param path str : the path in the configuration of the value to print.
1010 :param show_label boolean: if True, do a basic display.
1011 (useful for bash completion)
1012 :param logger Logger: the logger instance
1013 :param level int: The number of spaces to add before display.
1014 :param show_full_path :
1017 # Make sure that the path does not ends with a point
1018 if path.endswith('.'):
1021 # display all the path or not
1025 vname = path.split('.')[-1]
1027 # number of spaces before the display
1028 tab_level = " " * level
1030 # call to the function that gets the value of the path.
1032 val = config.getByPath(path)
1033 except Exception as e:
1034 logger.write(tab_level)
1035 logger.write("%s: ERROR %s\n" % (src.printcolors.printcLabel(vname),
1036 src.printcolors.printcError(str(e))))
1039 # in this case, display only the value
1041 logger.write(tab_level)
1042 logger.write("%s: " % src.printcolors.printcLabel(vname))
1044 # The case where the value has under values,
1045 # do a recursive call to the function
1046 if dir(val).__contains__('keys'):
1047 if show_label: logger.write("\n")
1048 for v in sorted(val.keys()):
1049 print_value(config, path + '.' + v, show_label, logger, level + 1)
1050 elif val.__class__ == src.pyconf.Sequence or isinstance(val, list):
1051 # in this case, value is a list (or a Sequence)
1052 if show_label: logger.write("\n")
1055 print_value(config, path + "[" + str(index) + "]",
1056 show_label, logger, level + 1)
1058 else: # case where val is just a str
1059 logger.write("%s\n" % val)
1061 def get_config_children(config, args):
1062 '''Gets the names of the children of the given parameter.
1063 Useful only for completion mechanism
1065 :param config Config: The configuration where to read the values
1066 :param args: The path in the config from which get the keys
1069 rootkeys = config.keys()
1072 # no parameter returns list of root keys
1076 pos = parent.rfind('.')
1078 # Case where there is only on key as parameter.
1080 vals = [m for m in rootkeys if m.startswith(parent)]
1082 # Case where there is a part from a key
1083 # for example VARS.us (for VARS.user)
1084 head = parent[0:pos]
1085 tail = parent[pos+1:]
1087 a = config.getByPath(head)
1088 if dir(a).__contains__('keys'):
1089 vals = map(lambda x: head + '.' + x,
1090 [m for m in a.keys() if m.startswith(tail)])
1094 for v in sorted(vals):
1095 sys.stdout.write("%s\n" % v)
1098 '''method that is called when salomeTools is called with --help option.
1100 :return: The text to display for the config command description.
1103 return _("The config command allows manipulation "
1104 "and operation on config files.\n\nexample:\nsat config "
1105 "SALOME-master --info ParaView")
1108 def run(args, runner, logger):
1109 '''method that is called when salomeTools is called with config parameter.
1112 (options, args) = parser.parse_args(args)
1114 # Only useful for completion mechanism : print the keys of the config
1116 get_config_children(runner.cfg, args)
1119 # case : print a value of the config
1121 if options.value == ".":
1122 # if argument is ".", print all the config
1123 for val in sorted(runner.cfg.keys()):
1124 print_value(runner.cfg, val, not options.no_label, logger)
1126 print_value(runner.cfg, options.value, not options.no_label, logger,
1127 level=0, show_full_path=False)
1129 # case : print a debug value of the config
1131 if options.debug == ".":
1132 # if argument is ".", print all the config
1133 res = DBG.indent(DBG.getStrConfigDbg(runner.cfg))
1134 logger.write("\nConfig of application %s:\n\n%s\n" % (runner.cfg.VARS.application, res))
1136 if options.debug[0] == ".": # accept ".PRODUCT.etc" as "PRODUCT.etc"
1137 od = options.debug[1:]
1141 aCode = "a = runner.cfg.%s" % od
1142 # https://stackoverflow.com/questions/15086040/behavior-of-exec-function-in-python-2-and-python-3
1143 aDict = {"runner": runner}
1144 exec(aCode, globals(), aDict)
1145 # DBG.write("globals()", globals(), True)
1146 # DBG.write("aDict", aDict, True)
1147 res = DBG.indent(DBG.getStrConfigDbg(aDict["a"]))
1148 logger.write("\nConfig.%s of application %s:\n\n%s\n" % (od, runner.cfg.VARS.application, res))
1149 except Exception as e:
1150 msg = "\nConfig.%s of application %s: Unknown pyconf key\n" % (od, runner.cfg.VARS.application)
1151 logger.write(src.printcolors.printcError(msg), 1)
1154 # case : edit user pyconf file or application file
1156 editor = runner.cfg.USER.editor
1157 if ('APPLICATION' not in runner.cfg and
1158 'open_application' not in runner.cfg): # edit user pyconf
1159 usercfg = osJoin(runner.cfg.VARS.personalDir,
1161 logger.write(_("Opening %s\n" % usercfg), 3)
1162 src.system.show_in_editor(editor, usercfg, logger)
1164 # search for file <application>.pyconf and open it
1165 for path in runner.cfg.PATHS.APPLICATIONPATH:
1166 pyconf_path = osJoin(path,
1167 runner.cfg.VARS.application + ".pyconf")
1168 if os.path.exists(pyconf_path):
1169 logger.write(_("Opening %s\n" % pyconf_path), 3)
1170 src.system.show_in_editor(editor, pyconf_path, logger)
1173 # case : give information about the product(s) in parameter
1174 if options.products:
1175 if options.info is not None:
1176 logger.warning('options.products %s overrides options.info %s' % (options.products, options.info))
1177 options.info = options.products
1180 # DBG.write("products", sorted(runner.cfg.APPLICATION.products.keys()), True)
1181 src.check_config_has_application(runner.cfg)
1182 taggedProducts = src.getProductNames(runner.cfg, options.info, logger)
1183 DBG.write("tagged products", sorted(taggedProducts))
1184 for prod in sorted(taggedProducts):
1185 if prod in runner.cfg.APPLICATION.products:
1187 if len(taggedProducts) > 1:
1188 logger.write("#################### ", 2)
1189 show_product_info(runner.cfg, prod, logger)
1190 except Exception as e:
1191 msg = "problem on product %s\n%s\n" % (prod, str(e))
1195 msg = _("%s is not a product of %s.\n") % \
1196 (prod, runner.cfg.VARS.application)
1198 #raise Exception(msg)
1200 # case : copy an existing <application>.pyconf
1201 # to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
1203 # product is required
1204 src.check_config_has_application( runner.cfg )
1206 # get application file path
1207 source = runner.cfg.VARS.application + '.pyconf'
1208 source_full_path = ""
1209 for path in runner.cfg.PATHS.APPLICATIONPATH:
1210 # ignore personal directory
1211 if path == runner.cfg.VARS.personalDir:
1213 # loop on all directories that can have pyconf applications
1214 zz = osJoin(path, source)
1215 if os.path.exists(zz):
1216 source_full_path = zz
1219 if len(source_full_path) == 0:
1220 raise src.SatException(_(
1221 "Config file for product %s not found\n") % source)
1224 # a name is given as parameter, use it
1226 elif 'copy_prefix' in runner.cfg.INTERNAL.config:
1228 dest = (runner.cfg.INTERNAL.config.copy_prefix
1229 + runner.cfg.VARS.application)
1231 # use same name as source
1232 dest = runner.cfg.VARS.application
1235 dest_file = osJoin(runner.cfg.VARS.personalDir,
1236 'Applications', dest + '.pyconf')
1237 if os.path.exists(dest_file):
1238 raise src.SatException(_("A personal application"
1239 " '%s' already exists") % dest)
1242 shutil.copyfile(source_full_path, dest_file)
1243 logger.write(_("%s has been created.\n") % dest_file)
1245 # case : display all the available pyconf applications
1248 # search in all directories that can have pyconf applications
1249 for path in runner.cfg.PATHS.APPLICATIONPATH:
1251 if not options.no_label:
1252 logger.write("------ %s\n" % src.printcolors.printcHeader(path))
1254 if not os.path.exists(path):
1255 logger.write(src.printcolors.printcError(_(
1256 "Directory not found")) + "\n")
1258 for f in sorted(os.listdir(path)):
1259 # ignore file that does not ends with .pyconf
1260 if not f.endswith('.pyconf'):
1263 appliname = f[:-len('.pyconf')]
1264 if appliname not in lproduct:
1265 lproduct.append(appliname)
1266 if path.startswith(runner.cfg.VARS.personalDir) \
1267 and not options.no_label:
1268 logger.write("%s*\n" % appliname)
1270 logger.write("%s\n" % appliname)
1274 # case: print all the products name of the application (internal use for completion)
1275 if options.completion:
1276 for product_name in runner.cfg.APPLICATION.products.keys():
1277 logger.write("%s\n" % product_name)
1279 # case : give a synthetic view of all patches used in the application
1280 if options.show_patchs:
1281 src.check_config_has_application(runner.cfg)
1282 # Print some informations
1283 logger.write(_('Patchs of application %s\n') %
1284 src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1285 logger.write("\n", 2, False)
1286 show_patchs(runner.cfg, logger)
1288 # case : give a synthetic view of all install directories used in the application
1289 if options.show_install:
1290 src.check_config_has_application(runner.cfg)
1291 # Print some informations
1292 logger.write(_('Installation directories of application %s\n') %
1293 src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1294 logger.write("\n", 2, False)
1295 show_install_dir(runner.cfg, logger)
1297 # case : give a synthetic view of all dependencies between products of the application
1298 if options.show_dependencies:
1299 src.check_config_has_application(runner.cfg)
1300 # Print some informations
1301 logger.write(_('List of run-time dependencies of the application %s, product by product\n') %
1302 src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1303 logger.write("\n", 2, False)
1304 show_dependencies(runner.cfg, options.products, logger)
1306 # case : give a synthetic view of all patches used in the application
1307 if options.show_properties:
1308 src.check_config_has_application(runner.cfg)
1310 # Print some informations
1311 logger.write(_('Properties of application %s\n') %
1312 src.printcolors.printcLabel(runner.cfg.VARS.application), 3)
1313 logger.write("\n", 2, False)
1314 show_properties(runner.cfg, logger)
1316 # check system prerequisites
1317 if options.check_system:
1318 check_install_system(runner.cfg, logger)