]> SALOME platform Git repositories - tools/sat.git/blob - src/product.py
Salome HOME
sat #17793 : correction bug sat prepare avec une base
[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             prod_info.archive_info.archive_name = arch_name #without path
278         else:
279             prod_info.archive_info.archive_name = arch_path
280
281         
282     # If the product compiles with a script, check the script existence
283     # and if it is executable
284     if product_has_script(prod_info):
285         # Check the compil_script key existence
286         if "compil_script" not in prod_info:
287             msg = _("""\
288 No compilation script found for the product %s.
289 Please provide a 'compil_script' key in its definition.""") % product_name
290             raise src.SatException(msg)
291         
292         # Get the path of the script file
293         # if windows supposed '.bat', if linux supposed '.sh'
294         # but user set extension script file in his pyconf as he wants, no obligation.
295         script = prod_info.compil_script
296         script_name = os.path.basename(script)
297         if script == script_name:
298             # Only a name is given. Search in the default directory
299             script_path = src.find_file_in_lpath(script_name, config.PATHS.PRODUCTPATH, "compil_scripts")
300             if not script_path:
301                 msg = _("Compilation script %s not found in") % script_name
302                 DBG.tofix(msg, config.PATHS.PRODUCTPATH, True) # say where searched
303                 script_path = "%s_(Not_Found_by_Sat!!)" % script_name
304             prod_info.compil_script = script_path
305
306        
307         # Check that the script is executable
308         if os.path.exists(prod_info.compil_script) and not os.access(prod_info.compil_script, os.X_OK):
309             DBG.tofix("Compilation script  file is not in 'execute mode'", prod_info.compil_script, True)
310     
311     # Get the full paths of all the patches
312     if product_has_patches(prod_info):
313         patches = []
314         try:
315           for patch in prod_info.patches:
316               patch_path = patch
317               # If only a filename, then search for the patch in the PRODUCTPATH
318               if os.path.basename(patch_path) == patch_path:
319                   # Search in the PRODUCTPATH/patches
320                   patch_path = src.find_file_in_lpath(patch,
321                                                       config.PATHS.PRODUCTPATH,
322                                                       "patches")
323                   if not patch_path:
324                       msg = _("Patch %(patch_name)s for %(prod_name)s not found:"
325                               "\n" % {"patch_name" : patch,
326                                        "prod_name" : prod_info.name}) 
327                       raise src.SatException(msg)
328               patches.append(patch_path)
329         except:
330           DBG.tofix("problem in prod_info.patches", prod_info)
331         prod_info.patches = patches
332
333     # Get the full paths of the environment scripts
334     if product_has_env_script(prod_info):
335         env_script_path = prod_info.environ.env_script
336         # If only a filename, then search for the environment script 
337         # in the PRODUCTPATH/env_scripts
338         if os.path.basename(env_script_path) == env_script_path:
339             # Search in the PRODUCTPATH/env_scripts
340             env_script_path = src.find_file_in_lpath(
341                                             prod_info.environ.env_script,
342                                             config.PATHS.PRODUCTPATH,
343                                             "env_scripts")
344             if not env_script_path:
345                 msg = _("Environment script %(env_name)s for %(prod_name)s not "
346                         "found.\n" % {"env_name" : env_script_path,
347                                        "prod_name" : prod_info.name}) 
348                 raise src.SatException(msg)
349
350         prod_info.environ.env_script = env_script_path
351     
352     if with_install_dir: 
353         # The variable with_install_dir is at false only for internal use 
354         # of the function get_install_dir
355         
356         # Save the install_dir key if there is any
357         if "install_dir" in prod_info and not "install_dir_save" in prod_info:
358             prod_info.install_dir_save = prod_info.install_dir
359         
360         # if it is not the first time the install_dir is computed, it means
361         # that install_dir_save exists and it has to be taken into account.
362         if "install_dir_save" in prod_info:
363             prod_info.install_dir = prod_info.install_dir_save
364         
365         # Set the install_dir key
366         prod_info.install_dir = get_install_dir(config, base, version, prod_info)
367                 
368     return prod_info
369
370 def get_product_section(config, product_name, version, section=None):
371     """Build the product description from the configuration
372     
373     :param config Config: The global configuration
374     :param product_name str: The product name
375     :param version str: The version of the product as 'V8_4_0', or else.
376     :param section str: The searched section (if not None, the section is 
377                         explicitly given
378     :return: The product description
379     :rtype: Config
380     """
381
382
383     #get product definition and determine if the incremental definition mode is activated
384     aProd = config.PRODUCTS[product_name]
385     if "default" in aProd and\
386        "properties" in aProd.default and\
387        "incremental" in aProd.default.properties and\
388        aProd.default.properties.incremental == "yes":
389         # in this (new) mode the definition of the product is given by the default section
390         # and is incremented by others.
391         is_incr=True
392     else:
393         # in this (historic) mode the definition of the product is given by a full unique section
394         is_incr=False
395
396     # decode version number
397     try:
398       versionMMP = VMMP.MinorMajorPatch(version)
399     except: # example setuptools raise "minor in major_minor_patch is not integer: '0_6c11'"
400       versionMMP = None
401
402     # if a section is explicitely specified we select it
403     if section:
404         if section not in aProd:
405             pi=None
406         # returns specific information for the given version
407         pi = aProd[section]
408         pi.section = section
409         pi.from_file = aProd.from_file
410
411     # If it exists, get the information of the product_version
412     # ex: 'version_V6_6_0' as salome version classical syntax
413     elif "version_" + version in aProd:
414         # returns specific information for the given version
415         pi = aProd["version_" + version]
416         pi.section = "version_" + version
417         pi.from_file = aProd.from_file
418
419     # Else, check if there is a description for multiple versions
420     else:
421         l_section_names = aProd.keys()
422         l_section_ranges = []
423         tagged = []
424         for name in l_section_names:
425           aRange = VMMP.getRange_majorMinorPatch(name)
426           if aRange is not None:
427             l_section_ranges.append((name, aRange))
428
429         if versionMMP is not None and len(l_section_ranges) > 0:
430           for name, (vmin, vmax) in l_section_ranges:
431             if versionMMP >= vmin and versionMMP <= vmax:
432               tagged.append((name, [vmin, vmax]))
433
434         if len(tagged) > 1:
435           DBG.write("multiple version ranges tagged for '%s', fix it" % version,
436                          PP.pformat(tagged))
437           pi=None
438         elif len(tagged) == 1: # ok
439           name, (vmin, vmax) = tagged[0]
440           pi = aProd[name]
441           pi.section = name
442           pi.from_file = aProd.from_file
443
444         # Else, get the standard informations
445         elif "default" in aProd:
446             # returns the generic information (given version not found)
447             pi = aProd.default
448             pi.section = "default"
449             pi.from_file = aProd.from_file
450         else:
451             pi=None
452
453     if is_incr:
454         # If the definition is incremental, we take the default section
455         # and then complete it with other sections : 
456         #   - default_win
457         #   - the selected section (pi)
458         #   - the selected _win section
459         prod_info=aProd["default"]
460         prod_info.from_file = aProd.from_file
461         prod_info.section = "default"
462         if src.architecture.is_windows() and "default_win" in aProd:
463             for key in aProd["default_win"]:
464                 prod_info[key]=aProd["default_win"][key]
465         if pi!=None and pi.section!="default":
466             # update prod_info with incremental definition contained in pi
467             for key in pi:
468                 prod_info[key]=pi[key]
469             win_section=pi.section+"_win"
470             if src.architecture.is_windows() and win_section in aProd:
471                 for key in aProd[win_section]:
472                     prod_info[key]=aProd[win_section][key]
473     else:
474         prod_info=pi
475
476     #DBG.write("product info returned for product %s with version %s and section %s" %\
477     #          (product_name, version, section), prod_info)
478     return prod_info
479     
480 def get_install_dir(config, base, version, prod_info):
481     """Compute the installation directory of a given product 
482     
483     :param config Config: The global configuration
484     :param base str: This corresponds to the value given by user in its 
485                      application.pyconf for the specific product. If "yes", the
486                      user wants the product to be in base. If "no", he wants the
487                      product to be in the application workdir
488     :param version str: The version of the product
489     :param product_info Config: The configuration specific to 
490                                the product
491     
492     :return: The path of the product installation
493     :rtype: str
494     """
495     install_dir = ""
496     in_base = False
497     # base : corresponds to what is specified in application pyconf (either from the global key, or from a product dict)
498     # prod_info.install_dir : corresponds to what is specified in product pyconf (usually "base" for prerequisites)
499     if (("install_dir" in prod_info and prod_info.install_dir == "base") 
500                                                             or base == "yes"):
501         in_base = True
502     # what was declared in application has precedence over what was said in product pyconf
503     # no_base="yes" has precedence over base == "yes"
504     if (base == "no" or ("no_base" in config.APPLICATION 
505                          and config.APPLICATION.no_base == "yes")):
506         in_base = False
507     
508     if in_base:
509         install_dir = get_base_install_dir(config, prod_info, version)
510     else:
511         if "install_dir" not in prod_info or prod_info.install_dir == "base":
512             # Set it to the default value (in application directory)
513             if ( src.appli_test_property(config,"single_install_dir", "yes") and 
514                  src.product.product_test_property(prod_info,"single_install_dir", "yes")):
515                 # when single_install_dir mode is activated in tha application
516                 # we use config.INTERNAL.config.single_install_dir for products 
517                 # having single_install_dir property
518                 install_dir = os.path.join(config.APPLICATION.workdir,
519                                            config.INTERNAL.config.install_dir,
520                                            config.INTERNAL.config.single_install_dir)
521             elif ( src.appli_test_property(config,"pip", "yes") and 
522                    src.product.product_test_property(prod_info,"pip", "yes") and
523                    src.appli_test_property(config,"pip_install_dir", "python") ):
524                 # when pip mode is activated in the application
525                 # and product is pip, and pip_install_dir is set to python 
526                 # we assume python in installed in install_dir/Python
527                 install_dir = os.path.join(config.APPLICATION.workdir,
528                                            config.INTERNAL.config.install_dir,
529                                            "Python")   
530             else:
531                 install_dir = os.path.join(config.APPLICATION.workdir,
532                                            config.INTERNAL.config.install_dir,
533                                            prod_info.name)
534         else:
535             install_dir = prod_info.install_dir
536
537     return install_dir
538
539 def get_base_install_dir(config, prod_info, version):
540     """Compute the installation directory of a product in base 
541     
542     :param config Config: The global configuration
543     :param product_info Config: The configuration specific to 
544                                the product
545     :param version str: The version of the product    
546     :return: The path of the product installation
547     :rtype: str
548     """    
549     base_path = src.get_base_path(config) 
550     prod_dir = os.path.join(base_path, prod_info.name + "-" + version)
551     if not os.path.exists(prod_dir):
552         return os.path.join(prod_dir, "config-1")
553     
554     exists, install_dir = check_config_exists(config, prod_dir, prod_info)
555     if exists:
556         return install_dir
557     
558     # Find the first config-<i> directory that is available in the product
559     # directory
560     found = False 
561     label = 1
562     while not found:
563         install_dir = os.path.join(prod_dir, "config-%i" % label)
564         if os.path.exists(install_dir):
565             label+=1
566         else:
567             found = True
568             
569     return install_dir
570
571 def add_compile_config_file(p_info, config):
572     '''Execute the proper configuration command(s)
573        in the product build directory.
574
575     :param p_info Config: The specific config of the product
576     :param config Config: The global configuration
577     '''
578     # Create the compile config
579     # DBG.write("add_compile_config_file", p_info, True)
580     res = src.pyconf.Config()
581     res.addMapping(p_info.name, src.pyconf.Mapping(res), "")
582     res[p_info.name]= p_info.version
583
584     for prod_name in p_info.depend:
585       if prod_name not in res:
586         res.addMapping(prod_name, src.pyconf.Mapping(res), "")
587       prod_dep_info = src.product.get_product_config(config, prod_name, False)
588       res[prod_name] = prod_dep_info.version
589     # Write it in the install directory of the product
590     # This file is for automatic reading/checking
591     # see check_config_exists method
592     afilename = CONFIG_FILENAME + p_info.name + ".pyconf"
593     aFile = os.path.join(p_info.install_dir, afilename)
594     with open(aFile, 'w') as f:
595       res.__save__(f)
596
597     # this file is not mandatory, is for human eye reading
598     afilename = PRODUCT_FILENAME + p_info.name + ".pyconf"
599     aFile = os.path.join(p_info.install_dir, afilename)
600     try:
601       with open(aFile, 'w') as f:
602         p_info.__save__(f, evaluated=True) # evaluated expressions mode
603     except:
604       # sometime some information cannot be evaluated.
605       # for example, in the context of non VCS archives, information on git server is not available.
606       DBG.write("Warning : sat was not able to evaluate and write down some information in file %s" % aFile)
607   
608
609 def check_config_exists(config, prod_dir, prod_info, verbose=False):
610     """\
611     Verify that the installation directory of a product in a base exists.
612     Check all the config-<i>/sat-config.py files found for correspondence
613     with current config and prod_info depend-version-tags
614     
615     :param config Config: The global configuration
616     :param prod_dir str: The product installation directory path 
617                          (without config-<i>)
618     :param product_info Config: The configuration specific to 
619                                the product
620     :return: True or false is the installation is found or not 
621              and if it is found, the path of the found installation
622     :rtype: (boolean, str)
623     """
624     # check if the directories or files of the directory corresponds to the
625     # directory installation of the product
626     if os.path.isdir(prod_dir):
627       l_dir_and_files = os.listdir(prod_dir)
628     else:
629       raise Exception("Inexisting directory '%s'" % prod_dir)
630
631     DBG.write("check_config_exists 000",  (prod_dir, l_dir_and_files), verbose)
632     DBG.write("check_config_exists 111",  prod_info, verbose)
633
634     for dir_or_file in l_dir_and_files:
635         oExpr = re.compile(config_expression)
636         if not(oExpr.search(dir_or_file)):
637             # in mode BASE, not config-<i>, not interesting
638             # DBG.write("not interesting", dir_or_file, True)
639             continue
640         # check if there is the file sat-config.pyconf file in the installation
641         # directory    
642         afilename = PRODUCT_FILENAME + prod_info.name + ".pyconf"
643         config_file = os.path.join(prod_dir, dir_or_file, afilename)
644         DBG.write("check_config_exists 222", config_file, verbose)
645         if not os.path.exists(config_file):
646             continue
647         
648         # check if there is the config described in the file corresponds the 
649         # dependencies of the product
650         config_corresponds = True    
651         compile_cfg = src.pyconf.Config(config_file)
652         for prod_dep in prod_info.depend:
653             # if the dependency is not in the config, 
654             # the config does not correspond
655             if prod_dep not in compile_cfg:
656                 config_corresponds = False
657                 break
658             else:
659                 prod_dep_info = get_product_config(config, prod_dep, False)
660                 # If the version of the dependency does not correspond, 
661                 # the config does not correspond
662                 if prod_dep_info.version != compile_cfg[prod_dep]:
663                     config_corresponds = False
664                     break
665
666         if config_corresponds:
667           for prod_name in compile_cfg:
668             # assume new compatibility with prod_name in sat-config.pyconf files
669             if prod_name == prod_info.name:
670               if prod_info.version == compile_cfg[prod_name]:
671                 DBG.write("check_config_exists OK 333", compile_cfg, verbose)
672                 pass
673               else: # no correspondence with newer with prod_name sat-config.pyconf files
674                 config_corresponds = False
675                 break
676             else:
677               # as old compatibility without prod_name sat-config.pyconf files
678               if prod_name not in prod_info.depend:
679                 # here there is an unexpected depend in an old compilation
680                 config_corresponds = False
681                 break
682         
683         if config_corresponds: # returns (and stops) at first correspondence found
684             DBG.write("check_config_exists OK 444", dir_or_file, verbose)
685             return True, os.path.join(prod_dir, dir_or_file)
686
687     # no correspondence found
688     return False, None
689             
690             
691     
692 def get_products_infos(lproducts, config):
693     """Get the specific configuration of a list of products
694     
695     :param lproducts List: The list of product names
696     :param config Config: The global configuration
697     :return: the list of tuples 
698              (product name, specific configuration of the product)
699     :rtype: [(str, Config)]
700     """
701     products_infos = []
702     # Loop on product names
703     for prod in lproducts:       
704         # Get the specific configuration of the product
705         prod_info = get_product_config(config, prod)
706         if prod_info is not None:
707             products_infos.append((prod, prod_info))
708         else:
709             msg = _("The %s product has no definition in the configuration.") % prod
710             raise src.SatException(msg)
711     return products_infos
712
713
714 def get_products_list(options, cfg, logger):
715     """
716     method that gives the product list with their informations from
717     configuration regarding the passed options.
718
719     :param options Options: The Options instance that stores the commands arguments
720     :param cfg Config: The global configuration
721     :param logger Logger: The logger instance to use for the display and logging
722     :return: The list of (product name, product_informations).
723     :rtype: List
724     """
725     # Get the products to be prepared, regarding the options
726     if options.products is None:
727         # No options, get all products sources
728         products = cfg.APPLICATION.products
729     else:
730         # if option --products, check that all products of the command line
731         # are present in the application.
732         """products = options.products
733         for p in products:
734             if p not in cfg.APPLICATION.products:
735                 raise src.SatException(_("Product %(product)s "
736                             "not defined in application %(application)s") %
737                         { 'product': p, 'application': cfg.VARS.application} )"""
738
739         products = src.getProductNames(cfg, options.products, logger)
740
741     # Construct the list of tuple containing
742     # the products name and their definition
743     resAll = src.product.get_products_infos(products, cfg)
744
745     # if the property option was passed, filter the list
746     if options.properties: # existing properties
747       ok = []
748       ko = []
749       res =[]
750       prop, value = options.properties # for example 'is_SALOME_module', 'yes'
751       for p_name, p_info in resAll:
752         try:
753           if p_info.properties[prop] == value:
754             res.append((p_name, p_info))
755             ok.append(p_name)
756           else:
757             ko.append(p_name)
758         except:
759           ko.append(p_name)
760
761       if len(ok) != len(resAll):
762         logger.trace("on properties %s\n products accepted:\n %s\n products rejected:\n %s\n" %
763                        (options.properties, PP.pformat(sorted(ok)), PP.pformat(sorted(ko))))
764       else:
765         logger.warning("properties %s\n seems useless with no products rejected" %
766                        (options.properties))
767     else:
768       res = resAll # not existing properties as all accepted
769
770     return res
771
772
773 def get_product_dependencies(config, product_info):
774     """\
775     Get recursively the list of products that are 
776     in the product_info dependencies
777     
778     :param config Config: The global configuration
779     :param product_info Config: The configuration specific to 
780                                the product
781     :return: the list of products in dependence
782     :rtype: list
783     """
784     if "depend" not in product_info or product_info.depend == []:
785         return []
786     res = []
787     for prod in product_info.depend:
788         if prod == product_info.name:
789             continue
790         if prod not in res:
791             res.append(prod)
792         prod_info = get_product_config(config, prod)
793         dep_prod = get_product_dependencies(config, prod_info)
794         for prod_in_dep in dep_prod:
795             if prod_in_dep not in res:
796                 res.append(prod_in_dep)
797     return res
798
799 def check_installation(config, product_info):
800     """\
801     Verify if a product is well installed. Checks install directory presence
802     and some additional files if it is defined in the config 
803     
804     :param product_info Config: The configuration specific to 
805                                the product
806     :return: True if it is well installed
807     :rtype: boolean
808     """
809     if not product_compiles(product_info):
810         return True
811
812     install_dir = product_info.install_dir
813     if ( (src.appli_test_property(config,"single_install_dir", "yes") and 
814           src.product.product_test_property(product_info,"single_install_dir", "yes")) or
815          (src.appli_test_property(config,"pip", "yes") and 
816           src.product.product_test_property(product_info,"pip", "yes") and
817           src.appli_test_property(config,"pip_install_dir", "python") ) ):
818         # if the product is installed in the single install dir, or in python (for pip managed products)
819         # we check the product file in state of the install directory.
820         filename = CONFIG_FILENAME + product_info.name + ".pyconf"
821         if not os.path.exists(os.path.join(install_dir, filename)): 
822             return False
823     else:
824         if not os.path.exists(install_dir):
825             return False
826
827     # check extra files if specified in present_files.install section
828     if ("present_files" in product_info and 
829         "install" in product_info.present_files):
830         for file_relative_path in product_info.present_files.install:
831             file_path = os.path.join(install_dir, file_relative_path)
832             if not os.path.exists(file_path):
833                 return False
834     return True
835
836 def check_source(product_info):
837     """Verify if a sources of product is preset. Checks source directory presence
838     
839     :param product_info Config: The configuration specific to 
840                                the product
841     :return: True if it is well installed
842     :rtype: boolean
843     """
844     source_dir = product_info.source_dir
845     if not os.path.exists(source_dir):
846         return False
847     if ("present_files" in product_info and 
848         "source" in product_info.present_files):
849         for file_relative_path in product_info.present_files.source:
850             file_path = os.path.join(source_dir, file_relative_path)
851             if not os.path.exists(file_path):
852                 return False
853     return True
854
855 def product_is_salome(product_info):
856     """Know if a product is a SALOME module
857     
858     :param product_info Config: The configuration specific to 
859                                the product
860     :return: True if the product is a SALOME module, else False
861     :rtype: boolean
862     """
863     return ("properties" in product_info and
864             "is_SALOME_module" in product_info.properties and
865             product_info.properties.is_SALOME_module == "yes")
866
867 def product_is_fixed(product_info):
868     """Know if a product is fixed
869     
870     :param product_info Config: The configuration specific to 
871                                the product
872     :return: True if the product is fixed, else False
873     :rtype: boolean
874     """
875     get_src = product_info.get_source
876     return get_src.lower() == 'fixed'
877
878 def product_is_native(product_info):
879     """Know if a product is native
880     
881     :param product_info Config: The configuration specific to 
882                                the product
883     :return: True if the product is native, else False
884     :rtype: boolean
885     """
886     get_src = product_info.get_source
887     return get_src.lower() == 'native'
888
889 def product_is_dev(product_info):
890     """Know if a product is in dev mode
891     
892     :param product_info Config: The configuration specific to 
893                                the product
894     :return: True if the product is in dev mode, else False
895     :rtype: boolean
896     """
897     dev = product_info.dev
898     res = (dev.lower() == 'yes')
899     DBG.write('product_is_dev %s' % product_info.name, res)
900     # if product_info.name == "XDATA": return True #test #10569
901     return res
902
903 def product_is_hpc(product_info):
904     """Know if a product is in hpc mode
905     
906     :param product_info Config: The configuration specific to 
907                                the product
908     :return: True if the product is in hpc mode, else False
909     :rtype: boolean
910     """
911     hpc = product_info.hpc
912     res = (hpc.lower() == 'yes')
913     return res
914
915 def product_is_debug(product_info):
916     """Know if a product is in debug mode
917     
918     :param product_info Config: The configuration specific to 
919                                the product
920     :return: True if the product is in debug mode, else False
921     :rtype: boolean
922     """
923     debug = product_info.debug
924     return debug.lower() == 'yes'
925
926 def product_is_verbose(product_info):
927     """Know if a product is in verbose mode
928     
929     :param product_info Config: The configuration specific to 
930                                the product
931     :return: True if the product is in verbose mode, else False
932     :rtype: boolean
933     """
934     verbose = product_info.verbose
935     return verbose.lower() == 'yes'
936
937 def product_is_autotools(product_info):
938     """Know if a product is compiled using the autotools
939     
940     :param product_info Config: The configuration specific to 
941                                the product
942     :return: True if the product is autotools, else False
943     :rtype: boolean
944     """
945     build_src = product_info.build_source
946     return build_src.lower() == 'autotools'
947
948 def product_is_cmake(product_info):
949     """Know if a product is compiled using the cmake
950     
951     :param product_info Config: The configuration specific to 
952                                the product
953     :return: True if the product is cmake, else False
954     :rtype: boolean
955     """
956     build_src = product_info.build_source
957     return build_src.lower() == 'cmake'
958
959 def product_is_vcs(product_info):
960     """Know if a product is download using git, svn or cvs (not archive)
961     
962     :param product_info Config: The configuration specific to 
963                                the product
964     :return: True if the product is vcs, else False
965     :rtype: boolean
966     """
967     return product_info.get_source in AVAILABLE_VCS
968
969 def product_is_smesh_plugin(product_info):
970     """Know if a product is a SMESH plugin
971     
972     :param product_info Config: The configuration specific to 
973                                the product
974     :return: True if the product is a SMESH plugin, else False
975     :rtype: boolean
976     """
977     return ("properties" in product_info and
978             "smesh_plugin" in product_info.properties and
979             product_info.properties.smesh_plugin == "yes")
980
981 def product_is_cpp(product_info):
982     """Know if a product is cpp
983     
984     :param product_info Config: The configuration specific to 
985                                the product
986     :return: True if the product is a cpp, else False
987     :rtype: boolean
988     """
989     return ("properties" in product_info and
990             "cpp" in product_info.properties and
991             product_info.properties.cpp == "yes")
992
993 def product_compiles(product_info):
994     """\
995     Know if a product compiles or not 
996     (some products do not have a compilation procedure)
997     
998     :param product_info Config: The configuration specific to 
999                                the product
1000     :return: True if the product compiles, else False
1001     :rtype: boolean
1002     """
1003     return not("properties" in product_info and
1004             "compilation" in product_info.properties and
1005             product_info.properties.compilation == "no")
1006
1007 def product_has_script(product_info):
1008     """Know if a product has a compilation script
1009     
1010     :param product_info Config: The configuration specific to 
1011                                the product
1012     :return: True if the product it has a compilation script, else False
1013     :rtype: boolean
1014     """
1015     if "build_source" not in product_info:
1016         # Native case
1017         return False
1018     build_src = product_info.build_source
1019     return build_src.lower() == 'script'
1020
1021 def product_has_env_script(product_info):
1022     """Know if a product has an environment script
1023     
1024     :param product_info Config: The configuration specific to 
1025                                the product
1026     :return: True if the product it has an environment script, else False
1027     :rtype: boolean
1028     """
1029     return "environ" in product_info and "env_script" in product_info.environ
1030
1031 def product_has_patches(product_info):
1032     """Know if a product has one or more patches
1033     
1034     :param product_info Config: The configuration specific to 
1035                                the product
1036     :return: True if the product has one or more patches
1037     :rtype: boolean
1038     """   
1039     res = ( "patches" in product_info and len(product_info.patches) > 0 )
1040     return res
1041
1042 def product_has_logo(product_info):
1043     """Know if a product has a logo (YACSGEN generate)
1044     
1045     :param product_info Config: The configuration specific to 
1046                                the product
1047     :return: The path of the logo if the product has a logo, else False
1048     :rtype: Str
1049     """
1050     if ("properties" in product_info and
1051             "logo" in product_info.properties):
1052         return product_info.properties.logo
1053     else:
1054         return False
1055
1056 def product_has_licence(product_info, path):
1057     """Find out if a product has a licence
1058     
1059     :param product_info Config: The configuration specific to the product
1060     :param path Str: The path where to search for the licence
1061     :return: The name of the licence file (the complete path if it is found in the path, else the name, else False
1062     :rtype: Str
1063     """
1064     if ("properties" in product_info and
1065             "licence" in product_info.properties):
1066         licence_name = product_info.properties.licence
1067         if len(path) > 0:
1068             # search for licence_name in path
1069             # a- consolidate the path into one signe string licence_path
1070             licence_path=path[0]
1071             for lpath in path[1:]:
1072                 licence_path=licence_path+":"+lpath
1073             licence_path_list=licence_path.split(":")
1074             licence_fullname = src.find_file_in_lpath(licence_name, licence_path_list)
1075             if licence_fullname:
1076                 return licence_fullname
1077
1078         # if the search of licence in path failed, we return its name (not the full path) 
1079         return licence_name
1080
1081     else:
1082         return False  # product has no licence
1083
1084 def product_has_salome_gui(product_info):
1085     """Know if a product has a SALOME gui
1086     
1087     :param product_info Config: The configuration specific to 
1088                                the product
1089     :return: True if the product has a SALOME gui, else False
1090     :rtype: Boolean
1091     """
1092     return ("properties" in product_info and
1093             "has_salome_gui" in product_info.properties and
1094             product_info.properties.has_salome_gui == "yes")
1095
1096 def product_is_mpi(product_info):
1097     """Know if a product has openmpi in its dependencies
1098     
1099     :param product_info Config: The configuration specific to 
1100                                the product
1101     :return: True if the product has openmpi inits dependencies
1102     :rtype: boolean
1103     """
1104     return "openmpi" in product_info.depend
1105
1106 def product_is_generated(product_info):
1107     """Know if a product is generated (YACSGEN)
1108     
1109     :param product_info Config: The configuration specific to 
1110                                the product
1111     :return: True if the product is generated
1112     :rtype: boolean
1113     """
1114     return ("properties" in product_info and
1115             "generate" in product_info.properties and
1116             product_info.properties.generate == "yes")
1117
1118 def product_is_compile_time(product_info):
1119     """Know if a product is only used at compile time
1120     
1121     :param product_info Config: The configuration specific to 
1122                                the product
1123     :return: True if the product is only used at compile time
1124     :rtype: boolean
1125     """
1126     return ("properties" in product_info and
1127             "compile_time" in product_info.properties and
1128             product_info.properties.compile_time == "yes")
1129
1130
1131 def product_test_property(product_info, property_name, property_value):
1132     """Generic function to test if a product has a property set to a value
1133     
1134     :param product_info Config: The configuration specific to 
1135                                the product
1136     :param property_name : The name of the property to check
1137     :param property_value : The value of the property to test
1138     :return: True if the product has the property and the property is set to property_value
1139     :rtype: boolean
1140     """
1141     # first check if product has the property
1142     if not ("properties" in product_info and
1143             property_name in product_info.properties):
1144         return False
1145   
1146     # then check to the property is set to property_value
1147     eval_expression = 'product_info.properties.%s == "%s"' % (property_name,property_value)
1148     result = eval(eval_expression)
1149     return result
1150
1151
1152
1153 def get_product_components(product_info):
1154     """Get the component list to generate with the product
1155     
1156     :param product_info Config: The configuration specific to 
1157                                the product
1158     :return: The list of names of the components
1159     :rtype: List
1160     
1161     """
1162     if not product_is_generated(product_info):
1163         return []
1164     
1165     compo_list = []
1166     if "component_name" in product_info:
1167         compo_list = product_info.component_name
1168     
1169         if isinstance(compo_list, str):
1170             compo_list = [ compo_list ]
1171
1172     return compo_list
1173 def product_is_wheel(product_info):
1174     """ tells whether a product is a wheel
1175     
1176     :param product_info Config: The configuration specific to 
1177                                the product
1178     :return: True if the product has a wheel, else False
1179     :rtype: Boolean
1180     """
1181     return ("properties" in product_info and
1182             "is_wheel" in product_info.properties and
1183             product_info.properties.is_wheel == "yes")
1184