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