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