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