Salome HOME
suppression du warning python 3 - le portage a bien avancé
[tools/sat.git] / src / product.py
index d20d9e4a52d485e61f1fb7764878376a76c3d78f..67b4d1dd73058cca4142a7beb3e9bc8f0a026383 100644 (file)
@@ -31,8 +31,8 @@ import src.versionMinorMajorPatch as VMMP
 
 AVAILABLE_VCS = ['git', 'svn', 'cvs']
 
-CONFIG_FILENAME = "sat-config.pyconf" # trace product depends version(s)
-PRODUCT_FILENAME = "sat-product.pyconf" # trace product compile config
+CONFIG_FILENAME = "sat-config-" # trace product depends version(s)
+PRODUCT_FILENAME = "sat-product-" # trace product compile config
 config_expression = "^config-\d+$"
 
 def get_product_config(config, product_name, with_install_dir=True):
@@ -71,6 +71,8 @@ def get_product_config(config, product_name, with_install_dir=True):
             dev = config.APPLICATION.dev
         if 'hpc' in config.APPLICATION:
             hpc = config.APPLICATION.hpc
+        if 'base' in config.APPLICATION:
+            base = config.APPLICATION.base
 
     # special case for which only the product name is mentionned 
     if isinstance(version, bool):
@@ -111,6 +113,8 @@ def get_product_config(config, product_name, with_install_dir=True):
         # Get the base if any
         if 'base' in dic_version:
             base = dic_version.base
+        elif 'base' in config.APPLICATION:
+            base = config.APPLICATION.base
 
         # Get the section if any
         if 'section' in dic_version:
@@ -177,6 +181,7 @@ def get_product_config(config, product_name, with_install_dir=True):
                 if depend in config.APPLICATION.products:
                     prod_info.depend.append(depend,'')
         
+
         # In case of a product get with a vcs, 
         # put the tag (equal to the version)
         if prod_info is not None and prod_info.get_source in AVAILABLE_VCS:
@@ -251,6 +256,8 @@ Please add a section in it.""") % {"1" : vv, "2" : prod_pyconf_path}
     prod_info.dev = dev
     prod_info.hpc = hpc
     prod_info.version = version
+    if base != 'maybe':
+        prod_info.base = base
 
     # Set the archive_info if the product is get in archive mode
     if prod_info.get_source == "archive":
@@ -260,6 +267,8 @@ Please add a section in it.""") % {"1" : vv, "2" : prod_pyconf_path}
                                  "")
         if "archive_name" in prod_info.archive_info: 
             arch_name = prod_info.archive_info.archive_name
+        elif "archive_prefix" in prod_info.archive_info:
+            arch_name = prod_info.archive_info.archive_prefix + "-" + version + ".tar.gz"
         else:
             # standard name
             arch_name = product_name + "-" + version + ".tar.gz"
@@ -269,9 +278,6 @@ Please add a section in it.""") % {"1" : vv, "2" : prod_pyconf_path}
         if not arch_path:
             # arch_path is not found. It may generate an error in sat source,
             #                         unless the archive is found in ftp serveur
-            msg = _("Archive %(1)s for %(2)s not found in config.PATHS.ARCHIVEPATH") % \
-                   {"1" : arch_name, "2" : prod_info.name}
-            DBG.tofix(msg, config.PATHS.ARCHIVEPATH)
             prod_info.archive_info.archive_name = arch_name #without path
         else:
             prod_info.archive_info.archive_name = arch_path
@@ -361,12 +367,12 @@ Please provide a 'compil_script' key in its definition.""") % product_name
             prod_info.install_dir = prod_info.install_dir_save
         
         # Set the install_dir key
-        prod_info.install_dir = get_install_dir(config, base, version, prod_info)
+        prod_info.install_dir = get_install_dir(config, version, prod_info)
                 
     return prod_info
 
-def get_product_section(config, product_name, version, section=None, verbose=False):
-    """Get the product description from the configuration
+def get_product_section(config, product_name, version, section=None):
+    """Build the product description from the configuration
     
     :param config Config: The global configuration
     :param product_name str: The product name
@@ -377,76 +383,105 @@ def get_product_section(config, product_name, version, section=None, verbose=Fal
     :rtype: Config
     """
 
-    # if section is not None, try to get the corresponding section
+
+    #get product definition and determine if the incremental definition mode is activated
     aProd = config.PRODUCTS[product_name]
+    if "default" in aProd and\
+       "properties" in aProd.default and\
+       "incremental" in aProd.default.properties and\
+       aProd.default.properties.incremental == "yes":
+        # in this (new) mode the definition of the product is given by the default section
+        # and is incremented by others.
+        is_incr=True
+    else:
+        # in this (historic) mode the definition of the product is given by a full unique section
+        is_incr=False
+
+    # decode version number
     try:
       versionMMP = VMMP.MinorMajorPatch(version)
     except: # example setuptools raise "minor in major_minor_patch is not integer: '0_6c11'"
       versionMMP = None
-    DBG.write("get_product_section for product %s '%s' as version '%s'" % (product_name, version, versionMMP),
-              (section, aProd.keys()), verbose)
-    # DBG.write("yoo1", aProd, True)
+
+    # if a section is explicitely specified we select it
     if section:
         if section not in aProd:
-            return None
+            pi=None
         # returns specific information for the given version
-        prod_info = aProd[section]
-        prod_info.section = section
-        prod_info.from_file = aProd.from_file
-        return prod_info
+        pi = aProd[section]
+        pi.section = section
+        pi.from_file = aProd.from_file
 
     # If it exists, get the information of the product_version
     # ex: 'version_V6_6_0' as salome version classical syntax
-    if "version_" + version in aProd:
-        DBG.write("found section for version_" + version, "", verbose)
+    elif "version_" + version in aProd:
         # returns specific information for the given version
-        prod_info = aProd["version_" + version]
-        prod_info.section = "version_" + version
-        prod_info.from_file = aProd.from_file
-        return prod_info
+        pi = aProd["version_" + version]
+        pi.section = "version_" + version
+        pi.from_file = aProd.from_file
 
     # Else, check if there is a description for multiple versions
-    l_section_names = aProd.keys()
-    l_section_ranges = []
-    tagged = []
-    for name in l_section_names:
-      # DBG.write("name", name,True)
-      aRange = VMMP.getRange_majorMinorPatch(name)
-      if aRange is not None:
-        DBG.write("found version range for section '%s'" % name, aRange, verbose)
-        l_section_ranges.append((name, aRange))
-
-    if versionMMP is not None and len(l_section_ranges) > 0:
-      for name, (vmin, vmax) in l_section_ranges:
-        if versionMMP >= vmin and versionMMP <= vmax:
-          tagged.append((name, [vmin, vmax]))
-
-    if len(tagged) > 1:
-      DBG.write("multiple version ranges tagged for '%s', fix it" % version,
-                     PP.pformat(tagged), True)
-      return None
-    if len(tagged) == 1: # ok
-      DBG.write("one version range tagged for '%s'" % version,
-                   PP.pformat(tagged), verbose)
-      name, (vmin, vmax) = tagged[0]
-      prod_info = aProd[name]
-      prod_info.section = name
-      prod_info.from_file = aProd.from_file
-      return prod_info
-
-    # Else, get the standard informations
-    if "default" in aProd:
-        # returns the generic information (given version not found)
-        prod_info = aProd.default
-        DBG.write("default tagged for '%s'" % version, prod_info, verbose)
-        prod_info.section = "default"
+    else:
+        l_section_names = aProd.keys()
+        l_section_ranges = []
+        tagged = []
+        for name in l_section_names:
+          aRange = VMMP.getRange_majorMinorPatch(name)
+          if aRange is not None:
+            l_section_ranges.append((name, aRange))
+
+        if versionMMP is not None and len(l_section_ranges) > 0:
+          for name, (vmin, vmax) in l_section_ranges:
+            if versionMMP >= vmin and versionMMP <= vmax:
+              tagged.append((name, [vmin, vmax]))
+
+        if len(tagged) > 1:
+          DBG.write("multiple version ranges tagged for '%s', fix it" % version,
+                         PP.pformat(tagged))
+          pi=None
+        elif len(tagged) == 1: # ok
+          name, (vmin, vmax) = tagged[0]
+          pi = aProd[name]
+          pi.section = name
+          pi.from_file = aProd.from_file
+
+        # Else, get the standard informations
+        elif "default" in aProd:
+            # returns the generic information (given version not found)
+            pi = aProd.default
+            pi.section = "default"
+            pi.from_file = aProd.from_file
+        else:
+            pi=None
+
+    if is_incr:
+        # If the definition is incremental, we take the default section
+        # and then complete it with other sections : 
+        #   - default_win
+        #   - the selected section (pi)
+        #   - the selected _win section
+        prod_info=aProd["default"]
         prod_info.from_file = aProd.from_file
-        return prod_info
-    
-    # if noting was found, return None
-    return None
+        prod_info.section = "default"
+        if src.architecture.is_windows() and "default_win" in aProd:
+            for key in aProd["default_win"]:
+                prod_info[key]=aProd["default_win"][key]
+        if pi!=None and pi.section!="default":
+            # update prod_info with incremental definition contained in pi
+            for key in pi:
+                prod_info[key]=pi[key]
+            win_section=pi.section+"_win"
+            if src.architecture.is_windows() and win_section in aProd:
+                for key in aProd[win_section]:
+                    prod_info[key]=aProd[win_section][key]
+    else:
+        prod_info=pi
+
+    #DBG.write("product info returned for product %s with version %s and section %s" %\
+    #          (product_name, version, section), prod_info)
+    return prod_info
     
-def get_install_dir(config, base, version, prod_info):
+def get_install_dir(config, version, prod_info):
     """Compute the installation directory of a given product 
     
     :param config Config: The global configuration
@@ -463,10 +498,16 @@ def get_install_dir(config, base, version, prod_info):
     """
     install_dir = ""
     in_base = False
-    if (("install_dir" in prod_info and prod_info.install_dir == "base") 
-                                                            or base == "yes"):
+    # prod_info.base : corresponds to what is specified in application pyconf (either from the global key, or from a product dict)
+    # prod_info.install_dir : corresponds to what is specified in product pyconf (usually "base" for prerequisites)
+    if ( ("install_dir" in prod_info and prod_info.install_dir == "base") 
+                                      or ("base" in prod_info  and prod_info.base != "no") ):
+        # a product goes in base if install_dir is set to base, or if product was declared based in application pyconf
         in_base = True
-    if (base == "no" or ("no_base" in config.APPLICATION 
+
+    # what was declared in application has precedence over what was said in product pyconf
+    # no_base="yes" has precedence over base == "yes"
+    if ( ("base" in prod_info  and prod_info.base == "no")  or ("no_base" in config.APPLICATION 
                          and config.APPLICATION.no_base == "yes")):
         in_base = False
     
@@ -475,9 +516,27 @@ def get_install_dir(config, base, version, prod_info):
     else:
         if "install_dir" not in prod_info or prod_info.install_dir == "base":
             # Set it to the default value (in application directory)
-            install_dir = os.path.join(config.APPLICATION.workdir,
-                                                "INSTALL",
-                                                prod_info.name)
+            if ( src.appli_test_property(config,"single_install_dir", "yes") and 
+                 src.product.product_test_property(prod_info,"single_install_dir", "yes")):
+                # when single_install_dir mode is activated in tha application
+                # we use config.INTERNAL.config.single_install_dir for products 
+                # having single_install_dir property
+                install_dir = os.path.join(config.APPLICATION.workdir,
+                                           config.INTERNAL.config.install_dir,
+                                           config.INTERNAL.config.single_install_dir)
+            elif ( src.appli_test_property(config,"pip", "yes") and 
+                   src.product.product_test_property(prod_info,"pip", "yes") and
+                   src.appli_test_property(config,"pip_install_dir", "python") ):
+                # when pip mode is activated in the application
+                # and product is pip, and pip_install_dir is set to python 
+                # we assume python in installed in install_dir/Python
+                install_dir = os.path.join(config.APPLICATION.workdir,
+                                           config.INTERNAL.config.install_dir,
+                                           "Python")   
+            else:
+                install_dir = os.path.join(config.APPLICATION.workdir,
+                                           config.INTERNAL.config.install_dir,
+                                           prod_info.name)
         else:
             install_dir = prod_info.install_dir
 
@@ -490,10 +549,29 @@ def get_base_install_dir(config, prod_info, version):
     :param product_info Config: The configuration specific to 
                                the product
     :param version str: The version of the product    
+    :param base str: This corresponds to the value given by user in its 
+                     application.pyconf for the specific product. If "yes", the
+                     user wants the product to be in base. If "no", he wants the
+                     product to be in the application workdir.
+                     if it contains something else, is is interpreted as the name 
+                     of a base we build for module load.
     :return: The path of the product installation
     :rtype: str
     """    
+    if ( src.appli_test_property(config,"pip", "yes") and 
+         src.product.product_test_property(prod_info,"pip", "yes") and
+         src.appli_test_property(config,"pip_install_dir", "python") ):
+         # when pip mode is activated in the application
+         # and product is pip, and pip_install_dir is set to python 
+        python_info=get_product_config(config, "Python")
+        return python_info.install_dir
+
     base_path = src.get_base_path(config) 
+    if "base" in prod_info and prod_info.base != "no" and prod_info.base != "yes":
+        # we are in the case of a named base
+        prod_dir = os.path.join(base_path, "apps", prod_info.base, prod_info.name, version)
+        return prod_dir
+    
     prod_dir = os.path.join(base_path, prod_info.name + "-" + version)
     if not os.path.exists(prod_dir):
         return os.path.join(prod_dir, "config-1")
@@ -536,27 +614,22 @@ def add_compile_config_file(p_info, config):
     # Write it in the install directory of the product
     # This file is for automatic reading/checking
     # see check_config_exists method
-    aFile = os.path.join(p_info.install_dir, CONFIG_FILENAME)
+    afilename = CONFIG_FILENAME + p_info.name + ".pyconf"
+    aFile = os.path.join(p_info.install_dir, afilename)
     with open(aFile, 'w') as f:
       res.__save__(f)
 
     # this file is not mandatory, is for human eye reading
-    aFile = os.path.join(p_info.install_dir, PRODUCT_FILENAME)
+    afilename = PRODUCT_FILENAME + p_info.name + ".pyconf"
+    aFile = os.path.join(p_info.install_dir, afilename)
     try:
       with open(aFile, 'w') as f:
         p_info.__save__(f, evaluated=True) # evaluated expressions mode
     except:
-      DBG.write("cannot evaluate product info - problem in file %s" % aFile, p_info, True)
-      # write DBG mode, as no problem if evaluation not possible
-      msg = """\
-# Some informations cannot be evaluated.
-# for example:
-# In the context of non VCS archives, information on git server is not available.
+      # sometime some information cannot be evaluated.
+      # for example, in the context of non VCS archives, information on git server is not available.
+      DBG.write("Warning : sat was not able to evaluate and write down some information in file %s" % aFile)
   
-"""
-      with open(aFile, 'w') as f:
-        f.write(msg)
-        f.write(DBG.getStrConfigDbg(p_info))
 
 def check_config_exists(config, prod_dir, prod_info, verbose=False):
     """\
@@ -591,7 +664,8 @@ def check_config_exists(config, prod_dir, prod_info, verbose=False):
             continue
         # check if there is the file sat-config.pyconf file in the installation
         # directory    
-        config_file = os.path.join(prod_dir, dir_or_file, CONFIG_FILENAME)
+        afilename = CONFIG_FILENAME + prod_info.name + ".pyconf"
+        config_file = os.path.join(prod_dir, dir_or_file, afilename)
         DBG.write("check_config_exists 222", config_file, verbose)
         if not os.path.exists(config_file):
             continue
@@ -707,7 +781,7 @@ def get_products_list(options, cfg, logger):
           else:
             ko.append(p_name)
         except:
-          ok.append(p_name)
+          ko.append(p_name)
 
       if len(ok) != len(resAll):
         logger.trace("on properties %s\n products accepted:\n %s\n products rejected:\n %s\n" %
@@ -747,7 +821,7 @@ def get_product_dependencies(config, product_info):
                 res.append(prod_in_dep)
     return res
 
-def check_installation(product_info):
+def check_installation(config, product_info):
     """\
     Verify if a product is well installed. Checks install directory presence
     and some additional files if it is defined in the config 
@@ -759,9 +833,23 @@ def check_installation(product_info):
     """
     if not product_compiles(product_info):
         return True
+
     install_dir = product_info.install_dir
-    if not os.path.exists(install_dir):
-        return False
+    if ( (src.appli_test_property(config,"single_install_dir", "yes") and 
+          src.product.product_test_property(product_info,"single_install_dir", "yes")) or
+         (src.appli_test_property(config,"pip", "yes") and 
+          src.product.product_test_property(product_info,"pip", "yes") and
+          src.appli_test_property(config,"pip_install_dir", "python") ) ):
+        # if the product is installed in the single install dir, or in python (for pip managed products)
+        # we check the product file in state of the install directory.
+        filename = CONFIG_FILENAME + product_info.name + ".pyconf"
+        if not os.path.exists(os.path.join(install_dir, filename)): 
+            return False
+    else:
+        if not os.path.exists(install_dir):
+            return False
+
+    # check extra files if specified in present_files.install section
     if ("present_files" in product_info and 
         "install" in product_info.present_files):
         for file_relative_path in product_info.present_files.install:
@@ -778,7 +866,6 @@ def check_source(product_info):
     :return: True if it is well installed
     :rtype: boolean
     """
-    DBG.write("check_source product_info", product_info)
     source_dir = product_info.source_dir
     if not os.path.exists(source_dir):
         return False
@@ -975,8 +1062,6 @@ def product_has_patches(product_info):
     :rtype: boolean
     """   
     res = ( "patches" in product_info and len(product_info.patches) > 0 )
-    DBG.write('product_has_patches %s' % product_info.name, res)
-    # if product_info.name == "XDATA": return True #test #10569
     return res
 
 def product_has_logo(product_info):
@@ -1055,6 +1140,41 @@ def product_is_generated(product_info):
             "generate" in product_info.properties and
             product_info.properties.generate == "yes")
 
+def product_is_compile_time(product_info):
+    """Know if a product is only used at compile time
+    
+    :param product_info Config: The configuration specific to 
+                               the product
+    :return: True if the product is only used at compile time
+    :rtype: boolean
+    """
+    return ("properties" in product_info and
+            "compile_time" in product_info.properties and
+            product_info.properties.compile_time == "yes")
+
+
+def product_test_property(product_info, property_name, property_value):
+    """Generic function to test if a product has a property set to a value
+    
+    :param product_info Config: The configuration specific to 
+                               the product
+    :param property_name : The name of the property to check
+    :param property_value : The value of the property to test
+    :return: True if the product has the property and the property is set to property_value
+    :rtype: boolean
+    """
+    # first check if product has the property
+    if not ("properties" in product_info and
+            property_name in product_info.properties):
+        return False
+  
+    # then check to the property is set to property_value
+    eval_expression = 'product_info.properties.%s == "%s"' % (property_name,property_value)
+    result = eval(eval_expression)
+    return result
+
+
+
 def get_product_components(product_info):
     """Get the component list to generate with the product
     
@@ -1075,3 +1195,15 @@ def get_product_components(product_info):
             compo_list = [ compo_list ]
 
     return compo_list
+def product_is_wheel(product_info):
+    """ tells whether a product is a wheel
+    
+    :param product_info Config: The configuration specific to 
+                               the product
+    :return: True if the product has a wheel, else False
+    :rtype: Boolean
+    """
+    return ("properties" in product_info and
+            "is_wheel" in product_info.properties and
+            product_info.properties.is_wheel == "yes")
+