Salome HOME
imp #12887 create sat-config.pyconf and sat-product.pyconf
[tools/sat.git] / src / product.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 """\
20 In this file are implemented the methods 
21 relative to the product notion of salomeTools
22 """
23
24 import os
25 import re
26 import pprint as PP
27
28 import src
29 import src.debug as DBG
30 import src.versionMinorMajorPatch as VMMP
31
32 AVAILABLE_VCS = ['git', 'svn', 'cvs']
33
34 CONFIG_FILENAME = "sat-config.pyconf" # trace product depends version(s)
35 PRODUCT_FILENAME = "sat-product.pyconf" # trace product compile config
36 config_expression = "^config-\d+$"
37
38 def get_product_config(config, product_name, with_install_dir=True):
39     """Get the specific configuration of a product from the global configuration
40     
41     :param config Config: The global configuration
42     :param product_name str: The name of the product
43     :param with_install_dir boolean: If false, do not provide an install 
44                                      directory (at false only for internal use 
45                                      of the function check_config_exists)
46     :return: the specific configuration of the product
47     :rtype: Config
48     """
49
50     # Get the version of the product from the application definition
51     version = config.APPLICATION.products[product_name]
52     # if no version, then take the default one defined in the application
53     if isinstance(version, bool): 
54         version = config.APPLICATION.tag      
55     
56     # Define debug and dev modes
57     # Get the tag if a dictionary is given in APPLICATION.products for the
58     # current product 
59     debug = 'no'
60     dev = 'no'
61     verbose = 'no'
62     base = 'maybe'
63     section = None
64     if isinstance(version, src.pyconf.Mapping):
65         dic_version = version
66         # Get the version/tag
67         if not 'tag' in dic_version:
68             version = config.APPLICATION.tag
69         else:
70             version = dic_version.tag
71         
72         # Get the debug if any
73         if 'debug' in dic_version:
74             debug = dic_version.debug
75         
76         # Get the verbose if any
77         if 'verbose' in dic_version:
78             verbose = dic_version.verbose
79         
80         # Get the dev if any
81         if 'dev' in dic_version:
82             dev = dic_version.dev
83         
84         # Get the base if any
85         if 'base' in dic_version:
86             base = dic_version.base
87
88         # Get the section if any
89         if 'section' in dic_version:
90             section = dic_version.section
91     
92     vv = version
93     # substitute some character with _ in order to get the correct definition
94     # in config.PRODUCTS. This is done because the pyconf tool does not handle
95     # the . and - characters 
96     for c in ".-": vv = vv.replace(c, "_")
97
98     prod_info = None
99     if product_name in config.PRODUCTS:
100         # Search for the product description in the configuration
101         prod_info = get_product_section(config, product_name, vv, section)
102         
103         # merge opt_depend in depend
104         if prod_info is not None and 'opt_depend' in prod_info:
105             for depend in prod_info.opt_depend:
106                 if depend in config.APPLICATION.products:
107                     prod_info.depend.append(depend,'')
108         
109         # In case of a product get with a vcs, 
110         # put the tag (equal to the version)
111         if prod_info is not None and prod_info.get_source in AVAILABLE_VCS:
112             
113             if prod_info.get_source == 'git':
114                 prod_info.git_info.tag = version
115             
116             if prod_info.get_source == 'svn':
117                 prod_info.svn_info.tag = version
118             
119             if prod_info.get_source == 'cvs':
120                 prod_info.cvs_info.tag = version
121         
122         # In case of a fixed product, 
123         # define the install_dir (equal to the version)
124         if prod_info is not None and os.path.isdir(version):
125             prod_info.install_dir = version
126             prod_info.get_source = "fixed"
127         
128         # Check if the product is defined as native in the application
129         if prod_info is not None:
130             if version == "native":
131                 prod_info.get_source = "native"
132             elif prod_info.get_source == "native":
133                 msg = _("The product %(prod)s has version %(ver)s but is "
134                         "declared as native in its definition" %
135                         { 'prod': prod_info.name, 'ver': version})
136                 raise src.SatException(msg)
137
138     # If there is no definition but the product is declared as native,
139     # construct a new definition containing only the get_source key
140     if prod_info is None and version == "native":
141         prod_info = src.pyconf.Config()
142         prod_info.name = product_name
143         prod_info.get_source = "native"
144
145     # If there is no definition but the product is fixed,
146     # construct a new definition containing only the product name
147     if prod_info is None and os.path.isdir(version):
148         prod_info = src.pyconf.Config()
149         prod_info.name = product_name
150         prod_info.get_source = "fixed"
151         prod_info.addMapping("environ", src.pyconf.Mapping(prod_info), "")
152
153
154     # If prod_info is still None, it means that there is no product definition
155     # in the config. The user has to provide it.
156     if prod_info is None:
157         prod_pyconf_path = src.find_file_in_lpath(product_name + ".pyconf",
158                                                   config.PATHS.PRODUCTPATH)
159         if not prod_pyconf_path:
160             msg = _("""\
161 No definition found for the product %(1)s.
162 Please create a %(1)s.pyconf file somewhere in:
163   %(2)s""") % {
164   "1": product_name,
165   "2": PP.pformat(config.PATHS.PRODUCTPATH) }
166         else:
167             msg = _("""\
168 No definition corresponding to the version %(1)s was found in the file:
169   %(2)s.
170 Please add a section in it.""") % {"1" : vv, "2" : prod_pyconf_path}
171         raise src.SatException(msg)
172     
173     # Set the debug, dev and version keys
174     prod_info.debug = debug
175     prod_info.verbose = verbose
176     prod_info.dev = dev
177     prod_info.version = version
178
179     # Set the archive_info if the product is get in archive mode
180     if prod_info.get_source == "archive":
181         if not "archive_info" in prod_info:
182             prod_info.addMapping("archive_info",
183                                  src.pyconf.Mapping(prod_info),
184                                  "")
185         if "archive_name" not in prod_info.archive_info: 
186             arch_name = product_name + "-" + version + ".tar.gz"
187             arch_path = src.find_file_in_lpath(arch_name,
188                                                config.PATHS.ARCHIVEPATH)
189             if not arch_path:
190                 msg = _("Archive %(1)s for %(2)s not found in config.PATHS.ARCHIVEPATH") % \
191                        {"1" : arch_name, "2" : prod_info.name}
192                 DBG.tofix(msg, config.PATHS.ARCHIVEPATH)
193                 prod_info.archive_info.archive_name = arch_name #without path
194                 # raise src.SatException(msg) #may be a warning, continue #8646
195             else:
196                 prod_info.archive_info.archive_name = arch_path
197         else:
198             if (os.path.basename(prod_info.archive_info.archive_name) == 
199                                         prod_info.archive_info.archive_name):
200                 arch_name = prod_info.archive_info.archive_name
201                 arch_path = src.find_file_in_lpath(
202                                             arch_name,
203                                             config.PATHS.ARCHIVEPATH)
204                 if not arch_path:
205                     msg = _("Archive %(1)s for %(2)s not found in config.PATHS.ARCHIVEPATH") % \
206                            {"1" : arch_name, "2" : prod_info.name}
207                     DBG.tofix(msg, config.PATHS.ARCHIVEPATH) #avoid 2 messages in compile
208                     prod_info.archive_info.archive_name = arch_name #without path
209                     # raise src.SatException(msg) #may be a warning, continue #8646
210                 else:
211                     prod_info.archive_info.archive_name = arch_path
212
213         
214     # If the product compiles with a script, check the script existence
215     # and if it is executable
216     if product_has_script(prod_info):
217         # Check the compil_script key existence
218         if "compil_script" not in prod_info:
219             msg = _("""\
220 No compilation script found for the product %s.
221 Please provide a 'compil_script' key in its definition.""") % product_name
222             raise src.SatException(msg)
223         
224         # Get the path of the script file
225         # if windows supposed '.bat', if linux supposed '.sh'
226         # but user set extension script file in his pyconf as he wants, no obligation.
227         script = prod_info.compil_script
228         script_name = os.path.basename(script)
229         if script == script_name:
230             # Only a name is given. Search in the default directory
231             script_path = src.find_file_in_lpath(script_name, config.PATHS.PRODUCTPATH, "compil_scripts")
232             if not script_path:
233                 msg = _("Compilation script %s not found in") % script_name
234                 DBG.tofix(msg, config.PATHS.PRODUCTPATH, True) # say where searched
235                 # avoid stop if sat prepare, script could be included in sources, only warning
236                 # raise src.SatException(msg)
237                 script_path = "*** Not Found: %s" % script_name
238             prod_info.compil_script = script_path
239
240        
241         # Check that the script is executable
242         if os.path.exists(prod_info.compil_script) and not os.access(prod_info.compil_script, os.X_OK):
243             #raise src.SatException(
244             #        _("Compilation script cannot be executed: %s") % 
245             #        prod_info.compil_script)
246             # just as warning, problem later...
247             DBG.tofix("Compilation script  file is not in 'execute mode'", prod_info.compil_script, True)
248     
249     # Get the full paths of all the patches
250     if product_has_patches(prod_info):
251         patches = []
252         try:
253           for patch in prod_info.patches:
254               patch_path = patch
255               # If only a filename, then search for the patch in the PRODUCTPATH
256               if os.path.basename(patch_path) == patch_path:
257                   # Search in the PRODUCTPATH/patches
258                   patch_path = src.find_file_in_lpath(patch,
259                                                       config.PATHS.PRODUCTPATH,
260                                                       "patches")
261                   if not patch_path:
262                       msg = _("Patch %(patch_name)s for %(prod_name)s not found:"
263                               "\n" % {"patch_name" : patch,
264                                        "prod_name" : prod_info.name}) 
265                       raise src.SatException(msg)
266               patches.append(patch_path)
267         except:
268           DBG.tofix("problem in prod_info.patches", prod_info)
269         prod_info.patches = patches
270
271     # Get the full paths of the environment scripts
272     if product_has_env_script(prod_info):
273         env_script_path = prod_info.environ.env_script
274         # If only a filename, then search for the environment script 
275         # in the PRODUCTPATH/env_scripts
276         if os.path.basename(env_script_path) == env_script_path:
277             # Search in the PRODUCTPATH/env_scripts
278             env_script_path = src.find_file_in_lpath(
279                                             prod_info.environ.env_script,
280                                             config.PATHS.PRODUCTPATH,
281                                             "env_scripts")
282             if not env_script_path:
283                 msg = _("Environment script %(env_name)s for %(prod_name)s not "
284                         "found.\n" % {"env_name" : env_script_path,
285                                        "prod_name" : prod_info.name}) 
286                 raise src.SatException(msg)
287
288         prod_info.environ.env_script = env_script_path
289     
290     if with_install_dir: 
291         # The variable with_install_dir is at false only for internal use 
292         # of the function get_install_dir
293         
294         # Save the install_dir key if there is any
295         if "install_dir" in prod_info and not "install_dir_save" in prod_info:
296             prod_info.install_dir_save = prod_info.install_dir
297         
298         # if it is not the first time the install_dir is computed, it means
299         # that install_dir_save exists and it has to be taken into account.
300         if "install_dir_save" in prod_info:
301             prod_info.install_dir = prod_info.install_dir_save
302         
303         # Set the install_dir key
304         prod_info.install_dir = get_install_dir(config, base, version, prod_info)
305                 
306     return prod_info
307
308 def get_product_section(config, product_name, version, section=None, verbose=False):
309     """Get the product description from the configuration
310     
311     :param config Config: The global configuration
312     :param product_name str: The product name
313     :param version str: The version of the product as 'V8_4_0', or else.
314     :param section str: The searched section (if not None, the section is 
315                         explicitly given
316     :return: The product description
317     :rtype: Config
318     """
319
320     # if section is not None, try to get the corresponding section
321     aProd = config.PRODUCTS[product_name]
322     try:
323       versionMMP = VMMP.MinorMajorPatch(version)
324     except: # example setuptools raise "minor in major_minor_patch is not integer: '0_6c11'"
325       versionMMP = None
326     DBG.write("get_product_section for product %s '%s' as version '%s'" % (product_name, version, versionMMP),
327               (section, aProd.keys()), verbose)
328     # DBG.write("yoo1", aProd, True)
329     if section:
330         if section not in aProd:
331             return None
332         # returns specific information for the given version
333         prod_info = aProd[section]
334         prod_info.section = section
335         prod_info.from_file = aProd.from_file
336         return prod_info
337
338     # If it exists, get the information of the product_version
339     # ex: 'version_V6_6_0' as salome version classical syntax
340     if "version_" + version in aProd:
341         DBG.write("found section for version_" + version, "", verbose)
342         # returns specific information for the given version
343         prod_info = aProd["version_" + version]
344         prod_info.section = "version_" + version
345         prod_info.from_file = aProd.from_file
346         return prod_info
347
348     # Else, check if there is a description for multiple versions
349     l_section_names = aProd.keys()
350     l_section_ranges = []
351     tagged = []
352     for name in l_section_names:
353       # DBG.write("name", name,True)
354       aRange = VMMP.getRange_majorMinorPatch(name)
355       if aRange is not None:
356         DBG.write("found version range for section '%s'" % name, aRange, verbose)
357         l_section_ranges.append((name, aRange))
358
359     if versionMMP is not None and len(l_section_ranges) > 0:
360       for name, (vmin, vmax) in l_section_ranges:
361         if versionMMP >= vmin and versionMMP <= vmax:
362           tagged.append((name, [vmin, vmax]))
363
364     if len(tagged) > 1:
365       DBG.write("multiple version ranges tagged for '%s', fix it" % version,
366                      PP.pformat(tagged), True)
367       return None
368     if len(tagged) == 1: # ok
369       DBG.write("one version range tagged for '%s'" % version,
370                    PP.pformat(tagged), verbose)
371       name, (vmin, vmax) = tagged[0]
372       prod_info = aProd[name]
373       prod_info.section = name
374       prod_info.from_file = aProd.from_file
375       return prod_info
376
377     # Else, get the standard informations
378     if "default" in aProd:
379         # returns the generic information (given version not found)
380         prod_info = aProd.default
381         DBG.write("default tagged for '%s'" % version, prod_info, verbose)
382         prod_info.section = "default"
383         prod_info.from_file = aProd.from_file
384         return prod_info
385     
386     # if noting was found, return None
387     return None
388     
389 def get_install_dir(config, base, version, prod_info):
390     """Compute the installation directory of a given product 
391     
392     :param config Config: The global configuration
393     :param base str: This corresponds to the value given by user in its 
394                      application.pyconf for the specific product. If "yes", the
395                      user wants the product to be in base. If "no", he wants the
396                      product to be in the application workdir
397     :param version str: The version of the product
398     :param product_info Config: The configuration specific to 
399                                the product
400     
401     :return: The path of the product installation
402     :rtype: str
403     """
404     install_dir = ""
405     in_base = False
406     if (("install_dir" in prod_info and prod_info.install_dir == "base") 
407                                                             or base == "yes"):
408         in_base = True
409     if (base == "no" or ("no_base" in config.APPLICATION 
410                          and config.APPLICATION.no_base == "yes")):
411         in_base = False
412     
413     if in_base:
414         install_dir = get_base_install_dir(config, prod_info, version)
415     else:
416         if "install_dir" not in prod_info or prod_info.install_dir == "base":
417             # Set it to the default value (in application directory)
418             install_dir = os.path.join(config.APPLICATION.workdir,
419                                                 "INSTALL",
420                                                 prod_info.name)
421         else:
422             install_dir = prod_info.install_dir
423
424     return install_dir
425
426 def get_base_install_dir(config, prod_info, version):
427     """Compute the installation directory of a product in base 
428     
429     :param config Config: The global configuration
430     :param product_info Config: The configuration specific to 
431                                the product
432     :param version str: The version of the product    
433     :return: The path of the product installation
434     :rtype: str
435     """    
436     base_path = src.get_base_path(config) 
437     prod_dir = os.path.join(base_path, prod_info.name + "-" + version)
438     if not os.path.exists(prod_dir):
439         return os.path.join(prod_dir, "config-1")
440     
441     exists, install_dir = check_config_exists(config, prod_dir, prod_info)
442     if exists:
443         return install_dir
444     
445     # Find the first config-<i> directory that is available in the product
446     # directory
447     found = False 
448     label = 1
449     while not found:
450         install_dir = os.path.join(prod_dir, "config-%i" % label)
451         if os.path.exists(install_dir):
452             label+=1
453         else:
454             found = True
455             
456     return install_dir
457
458 def add_compile_config_file(p_info, config):
459     '''Execute the proper configuration command(s)
460        in the product build directory.
461
462     :param p_info Config: The specific config of the product
463     :param config Config: The global configuration
464     '''
465     # Create the compile config
466     # DBG.write("add_compile_config_file", p_info, True)
467     res = src.pyconf.Config()
468     res.addMapping(p_info.name, src.pyconf.Mapping(res), "")
469     res[p_info.name]= p_info.version
470
471     for prod_name in p_info.depend:
472       if prod_name not in res:
473         res.addMapping(prod_name, src.pyconf.Mapping(res), "")
474       prod_dep_info = src.product.get_product_config(config, prod_name, False)
475       res[prod_name] = prod_dep_info.version
476     # Write it in the install directory of the product
477     # This file is for automatic reading/checking
478     # see check_config_exists method
479     aFile = os.path.join(p_info.install_dir, CONFIG_FILENAME)
480     with open(aFile, 'w') as f:
481       res.__save__(f)
482
483     # this file is for human eye reading
484     aFile = os.path.join(p_info.install_dir, PRODUCT_FILENAME)
485     with open(aFile, 'w') as f:
486       # f.write(DBG.getStrConfigDbg(p_info)) # debug mode
487       p_info.__save__(f, evaluated=True) # evaluated expressions mode
488
489
490 def check_config_exists(config, prod_dir, prod_info, verbose=False):
491     """\
492     Verify that the installation directory of a product in a base exists.
493     Check all the config-<i>/sat-config.py files found for correspondence
494     with current config and prod_info depend-version-tags
495     
496     :param config Config: The global configuration
497     :param prod_dir str: The product installation directory path 
498                          (without config-<i>)
499     :param product_info Config: The configuration specific to 
500                                the product
501     :return: True or false is the installation is found or not 
502              and if it is found, the path of the found installation
503     :rtype: (boolean, str)
504     """
505     # check if the directories or files of the directory corresponds to the
506     # directory installation of the product
507     if os.path.isdir(prod_dir):
508       l_dir_and_files = os.listdir(prod_dir)
509     else:
510       raise Exception("Inexisting directory '%s'" % prod_dir)
511
512     DBG.write("check_config_exists 000",  (prod_dir, l_dir_and_files), verbose)
513     DBG.write("check_config_exists 111",  prod_info, verbose)
514
515     for dir_or_file in l_dir_and_files:
516         oExpr = re.compile(config_expression)
517         if not(oExpr.search(dir_or_file)):
518             # in mode BASE, not config-<i>, not interesting
519             # DBG.write("not interesting", dir_or_file, True)
520             continue
521         # check if there is the file sat-config.pyconf file in the installation
522         # directory    
523         config_file = os.path.join(prod_dir, dir_or_file, CONFIG_FILENAME)
524         DBG.write("check_config_exists 222", config_file, verbose)
525         if not os.path.exists(config_file):
526             continue
527         
528         # If there is no dependency, it is the right path
529         if len(prod_info.depend)==0:
530             compile_cfg = src.pyconf.Config(config_file)
531             if len(compile_cfg) == 0:
532                 return True, os.path.join(prod_dir, dir_or_file)
533             continue
534
535         # check if there is the config described in the file corresponds the 
536         # dependencies of the product
537         config_corresponds = True    
538         compile_cfg = src.pyconf.Config(config_file)
539         for prod_dep in prod_info.depend:
540             # if the dependency is not in the config, 
541             # the config does not correspond
542             if prod_dep not in compile_cfg:
543                 config_corresponds = False
544                 break
545             else:
546                 prod_dep_info = get_product_config(config, prod_dep, False)
547                 # If the version of the dependency does not correspond, 
548                 # the config does not correspond
549                 if prod_dep_info.version != compile_cfg[prod_dep]:
550                     config_corresponds = False
551                     break
552
553         if config_corresponds:
554           for prod_name in compile_cfg:
555             # assume new compatibility with prod_name in sat-config.pyconf files
556             if prod_name == prod_info.name:
557               if prod_info.version == compile_cfg[prod_name]:
558                 DBG.write("check_config_exists OK 333", compile_cfg, verbose)
559                 pass
560               else: # no correspondence with newer with prod_name sat-config.pyconf files
561                 config_corresponds = False
562                 break
563             else:
564               # as old compatibility without prod_name sat-config.pyconf files
565               if prod_name not in prod_info.depend:
566                 # here there is an unexpected depend in an old compilation
567                 config_corresponds = False
568                 break
569         
570         if config_corresponds: # returns (and stops) at first correspondence found
571             DBG.write("check_config_exists OK 444", dir_or_file, verbose)
572             return True, os.path.join(prod_dir, dir_or_file)
573
574     # no correspondence found
575     return False, None
576             
577             
578     
579 def get_products_infos(lproducts, config):
580     """Get the specific configuration of a list of products
581     
582     :param lproducts List: The list of product names
583     :param config Config: The global configuration
584     :return: the list of tuples 
585              (product name, specific configuration of the product)
586     :rtype: [(str, Config)]
587     """
588     products_infos = []
589     # Loop on product names
590     for prod in lproducts:       
591         # Get the specific configuration of the product
592         prod_info = get_product_config(config, prod)
593         if prod_info is not None:
594             products_infos.append((prod, prod_info))
595         else:
596             msg = _("The %s product has no definition in the configuration.") % prod
597             raise src.SatException(msg)
598     return products_infos
599
600
601 def get_products_list(options, cfg, logger):
602     """
603     method that gives the product list with their informations from
604     configuration regarding the passed options.
605
606     :param options Options: The Options instance that stores the commands arguments
607     :param cfg Config: The global configuration
608     :param logger Logger: The logger instance to use for the display and logging
609     :return: The list of (product name, product_informations).
610     :rtype: List
611     """
612     # Get the products to be prepared, regarding the options
613     if options.products is None:
614         # No options, get all products sources
615         products = cfg.APPLICATION.products
616     else:
617         # if option --products, check that all products of the command line
618         # are present in the application.
619         """products = options.products
620         for p in products:
621             if p not in cfg.APPLICATION.products:
622                 raise src.SatException(_("Product %(product)s "
623                             "not defined in application %(application)s") %
624                         { 'product': p, 'application': cfg.VARS.application} )"""
625
626         products = src.getProductNames(cfg, options.products, logger)
627
628     # Construct the list of tuple containing
629     # the products name and their definition
630     resAll = src.product.get_products_infos(products, cfg)
631
632     # if the property option was passed, filter the list
633     if options.properties: # existing properties
634       ok = []
635       ko = []
636       res =[]
637       prop, value = options.properties # for example 'is_SALOME_module', 'yes'
638       for p_name, p_info in resAll:
639         try:
640           if p_info.properties[prop] == value:
641             res.append((p_name, p_info))
642             ok.append(p_name)
643           else:
644             ko.append(p_name)
645         except:
646           ok.append(p_name)
647
648       if len(ok) != len(resAll):
649         logger.trace("on properties %s\n products accepted:\n %s\n products rejected:\n %s\n" %
650                        (options.properties, PP.pformat(sorted(ok)), PP.pformat(sorted(ko))))
651       else:
652         logger.warning("properties %s\n seems useless with no products rejected" %
653                        (options.properties))
654     else:
655       res = resAll # not existing properties as all accepted
656
657
658     ok = []
659     ko = []
660     products_infos = []
661     for p_name, p_info in res:
662       try:
663         if src.product.product_is_native(p_info) or src.product.product_is_fixed(p_info):
664           ko.append(p_name)
665         else:
666           products_infos.append((p_name, p_info))
667           ok.append(p_name)
668       except:
669         msg = "problem on 'is_native' or 'is_fixed' for product %s" % p_name
670         raise Exception(msg)
671
672     if len(ko) > 0:
673       logger.warning("on is_native or is_fixed\n products accepted:\n %s\n products rejected:\n %s\n" %
674                     (PP.pformat(sorted(ok)), PP.pformat(sorted(ko))))
675
676     logger.debug("products selected:\n %s\n" % PP.pformat(sorted(ok)))
677
678     return res
679
680
681 def get_product_dependencies(config, product_info):
682     """\
683     Get recursively the list of products that are 
684     in the product_info dependencies
685     
686     :param config Config: The global configuration
687     :param product_info Config: The configuration specific to 
688                                the product
689     :return: the list of products in dependence
690     :rtype: list
691     """
692     if "depend" not in product_info or product_info.depend == []:
693         return []
694     res = []
695     for prod in product_info.depend:
696         if prod == product_info.name:
697             continue
698         if prod not in res:
699             res.append(prod)
700         prod_info = get_product_config(config, prod)
701         dep_prod = get_product_dependencies(config, prod_info)
702         for prod_in_dep in dep_prod:
703             if prod_in_dep not in res:
704                 res.append(prod_in_dep)
705     return res
706
707 def check_installation(product_info):
708     """\
709     Verify if a product is well installed. Checks install directory presence
710     and some additional files if it is defined in the config 
711     
712     :param product_info Config: The configuration specific to 
713                                the product
714     :return: True if it is well installed
715     :rtype: boolean
716     """
717     if not product_compiles(product_info):
718         return True
719     install_dir = product_info.install_dir
720     if not os.path.exists(install_dir):
721         return False
722     if ("present_files" in product_info and 
723         "install" in product_info.present_files):
724         for file_relative_path in product_info.present_files.install:
725             file_path = os.path.join(install_dir, file_relative_path)
726             if not os.path.exists(file_path):
727                 return False
728     return True
729
730 def check_source(product_info):
731     """Verify if a sources of product is preset. Checks source directory presence
732     
733     :param product_info Config: The configuration specific to 
734                                the product
735     :return: True if it is well installed
736     :rtype: boolean
737     """
738     DBG.write("check_source product_info", product_info)
739     source_dir = product_info.source_dir
740     if not os.path.exists(source_dir):
741         return False
742     if ("present_files" in product_info and 
743         "source" in product_info.present_files):
744         for file_relative_path in product_info.present_files.source:
745             file_path = os.path.join(source_dir, file_relative_path)
746             if not os.path.exists(file_path):
747                 return False
748     return True
749
750 def product_is_salome(product_info):
751     """Know if a product is a SALOME module
752     
753     :param product_info Config: The configuration specific to 
754                                the product
755     :return: True if the product is a SALOME module, else False
756     :rtype: boolean
757     """
758     return ("properties" in product_info and
759             "is_SALOME_module" in product_info.properties and
760             product_info.properties.is_SALOME_module == "yes")
761
762 def product_is_fixed(product_info):
763     """Know if a product is fixed
764     
765     :param product_info Config: The configuration specific to 
766                                the product
767     :return: True if the product is fixed, else False
768     :rtype: boolean
769     """
770     get_src = product_info.get_source
771     return get_src.lower() == 'fixed'
772
773 def product_is_native(product_info):
774     """Know if a product is native
775     
776     :param product_info Config: The configuration specific to 
777                                the product
778     :return: True if the product is native, else False
779     :rtype: boolean
780     """
781     get_src = product_info.get_source
782     return get_src.lower() == 'native'
783
784 def product_is_dev(product_info):
785     """Know if a product is in dev mode
786     
787     :param product_info Config: The configuration specific to 
788                                the product
789     :return: True if the product is in dev mode, else False
790     :rtype: boolean
791     """
792     dev = product_info.dev
793     res = (dev.lower() == 'yes')
794     DBG.write('product_is_dev %s' % product_info.name, res)
795     # if product_info.name == "XDATA": return True #test #10569
796     return res
797
798 def product_is_debug(product_info):
799     """Know if a product is in debug mode
800     
801     :param product_info Config: The configuration specific to 
802                                the product
803     :return: True if the product is in debug mode, else False
804     :rtype: boolean
805     """
806     debug = product_info.debug
807     return debug.lower() == 'yes'
808
809 def product_is_verbose(product_info):
810     """Know if a product is in verbose mode
811     
812     :param product_info Config: The configuration specific to 
813                                the product
814     :return: True if the product is in verbose mode, else False
815     :rtype: boolean
816     """
817     verbose = product_info.verbose
818     return verbose.lower() == 'yes'
819
820 def product_is_autotools(product_info):
821     """Know if a product is compiled using the autotools
822     
823     :param product_info Config: The configuration specific to 
824                                the product
825     :return: True if the product is autotools, else False
826     :rtype: boolean
827     """
828     build_src = product_info.build_source
829     return build_src.lower() == 'autotools'
830
831 def product_is_cmake(product_info):
832     """Know if a product is compiled using the cmake
833     
834     :param product_info Config: The configuration specific to 
835                                the product
836     :return: True if the product is cmake, else False
837     :rtype: boolean
838     """
839     build_src = product_info.build_source
840     return build_src.lower() == 'cmake'
841
842 def product_is_vcs(product_info):
843     """Know if a product is download using git, svn or cvs (not archive)
844     
845     :param product_info Config: The configuration specific to 
846                                the product
847     :return: True if the product is vcs, else False
848     :rtype: boolean
849     """
850     return product_info.get_source in AVAILABLE_VCS
851
852 def product_is_smesh_plugin(product_info):
853     """Know if a product is a SMESH plugin
854     
855     :param product_info Config: The configuration specific to 
856                                the product
857     :return: True if the product is a SMESH plugin, else False
858     :rtype: boolean
859     """
860     return ("properties" in product_info and
861             "smesh_plugin" in product_info.properties and
862             product_info.properties.smesh_plugin == "yes")
863
864 def product_is_cpp(product_info):
865     """Know if a product is cpp
866     
867     :param product_info Config: The configuration specific to 
868                                the product
869     :return: True if the product is a cpp, else False
870     :rtype: boolean
871     """
872     return ("properties" in product_info and
873             "cpp" in product_info.properties and
874             product_info.properties.cpp == "yes")
875
876 def product_compiles(product_info):
877     """\
878     Know if a product compiles or not 
879     (some products do not have a compilation procedure)
880     
881     :param product_info Config: The configuration specific to 
882                                the product
883     :return: True if the product compiles, else False
884     :rtype: boolean
885     """
886     return not("properties" in product_info and
887             "compilation" in product_info.properties and
888             product_info.properties.compilation == "no")
889
890 def product_has_script(product_info):
891     """Know if a product has a compilation script
892     
893     :param product_info Config: The configuration specific to 
894                                the product
895     :return: True if the product it has a compilation script, else False
896     :rtype: boolean
897     """
898     if "build_source" not in product_info:
899         # Native case
900         return False
901     build_src = product_info.build_source
902     return build_src.lower() == 'script'
903
904 def product_has_env_script(product_info):
905     """Know if a product has an environment script
906     
907     :param product_info Config: The configuration specific to 
908                                the product
909     :return: True if the product it has an environment script, else False
910     :rtype: boolean
911     """
912     return "environ" in product_info and "env_script" in product_info.environ
913
914 def product_has_patches(product_info):
915     """Know if a product has one or more patches
916     
917     :param product_info Config: The configuration specific to 
918                                the product
919     :return: True if the product has one or more patches
920     :rtype: boolean
921     """   
922     res = ( "patches" in product_info and len(product_info.patches) > 0 )
923     DBG.write('product_has_patches %s' % product_info.name, res)
924     # if product_info.name == "XDATA": return True #test #10569
925     return res
926
927 def product_has_logo(product_info):
928     """Know if a product has a logo (YACSGEN generate)
929     
930     :param product_info Config: The configuration specific to 
931                                the product
932     :return: The path of the logo if the product has a logo, else False
933     :rtype: Str
934     """
935     if ("properties" in product_info and
936             "logo" in product_info.properties):
937         return product_info.properties.logo
938     else:
939         return False
940
941 def product_has_salome_gui(product_info):
942     """Know if a product has a SALOME gui
943     
944     :param product_info Config: The configuration specific to 
945                                the product
946     :return: True if the product has a SALOME gui, else False
947     :rtype: Boolean
948     """
949     return ("properties" in product_info and
950             "has_salome_gui" in product_info.properties and
951             product_info.properties.has_salome_gui == "yes")
952
953 def product_is_mpi(product_info):
954     """Know if a product has openmpi in its dependencies
955     
956     :param product_info Config: The configuration specific to 
957                                the product
958     :return: True if the product has openmpi inits dependencies
959     :rtype: boolean
960     """
961     return "openmpi" in product_info.depend
962
963 def product_is_generated(product_info):
964     """Know if a product is generated (YACSGEN)
965     
966     :param product_info Config: The configuration specific to 
967                                the product
968     :return: True if the product is generated
969     :rtype: boolean
970     """
971     return ("properties" in product_info and
972             "generate" in product_info.properties and
973             product_info.properties.generate == "yes")
974
975 def get_product_components(product_info):
976     """Get the component list to generate with the product
977     
978     :param product_info Config: The configuration specific to 
979                                the product
980     :return: The list of names of the components
981     :rtype: List
982     
983     """
984     if not product_is_generated(product_info):
985         return []
986     
987     compo_list = []
988     if "component_name" in product_info:
989         compo_list = product_info.component_name
990     
991         if isinstance(compo_list, str):
992             compo_list = [ compo_list ]
993
994     return compo_list