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