X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2Fproduct.py;h=4c72a43657f385fd5729358b2ff00416a0d74032;hb=28cebd157f9d39920d0480232e7c361716ca45bb;hp=08c42043f5adc6f7e5732822c0b674555a325ea3;hpb=4db78e29b5066408cde46dee5e5d89ae2731e7e7;p=tools%2Fsat.git diff --git a/src/product.py b/src/product.py index 08c4204..4c72a43 100644 --- a/src/product.py +++ b/src/product.py @@ -15,21 +15,28 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -'''In this file are implemented the methods - relative to the product notion of salomeTools -''' + +"""\ +In this file are implemented the methods +relative to the product notion of salomeTools +""" import os import re +import pprint as PP import src +import src.debug as DBG +import src.versionMinorMajorPatch as VMMP AVAILABLE_VCS = ['git', 'svn', 'cvs'] + +CONFIG_FILENAME = "sat-config-" # trace product depends version(s) +PRODUCT_FILENAME = "sat-product-" # trace product compile config config_expression = "^config-\d+$" -VERSION_DELIMITER = "_to_" def get_product_config(config, product_name, with_install_dir=True): - '''Get the specific configuration of a product from the global configuration + """Get the specific configuration of a product from the global configuration :param config Config: The global configuration :param product_name str: The name of the product @@ -38,21 +45,39 @@ def get_product_config(config, product_name, with_install_dir=True): of the function check_config_exists) :return: the specific configuration of the product :rtype: Config - ''' - + """ + # Get the version of the product from the application definition version = config.APPLICATION.products[product_name] - # if no version, then take the default one defined in the application - if isinstance(version, bool): - version = config.APPLICATION.tag # Define debug and dev modes # Get the tag if a dictionary is given in APPLICATION.products for the # current product debug = 'no' dev = 'no' + hpc = 'no' + verbose = 'no' base = 'maybe' section = None + + # if no version, then take the default one defined in the application + if isinstance(version, bool) or isinstance(version, str): + # in this case tag is mandatory, not debug, verbose, dev + if 'debug' in config.APPLICATION: + debug = config.APPLICATION.debug + if 'verbose' in config.APPLICATION: + verbose = config.APPLICATION.verbose + if 'dev' in config.APPLICATION: + 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): + version = config.APPLICATION.tag + if isinstance(version, src.pyconf.Mapping): dic_version = version # Get the version/tag @@ -64,36 +89,99 @@ def get_product_config(config, product_name, with_install_dir=True): # Get the debug if any if 'debug' in dic_version: debug = dic_version.debug + elif 'debug' in config.APPLICATION: + debug = config.APPLICATION.debug + + # Get the verbose if any + if 'verbose' in dic_version: + verbose = dic_version.verbose + elif 'verbose' in config.APPLICATION: + verbose = config.APPLICATION.verbose # Get the dev if any if 'dev' in dic_version: dev = dic_version.dev + elif 'dev' in config.APPLICATION: + dev = config.APPLICATION.dev + # Get the hpc if any + if 'hpc' in dic_version: + hpc = dic_version.hpc + elif 'hpc' in config.APPLICATION: + hpc = config.APPLICATION.hpc + # 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: section = dic_version.section + # this case occur when version is overwritten, cf sat # 8897 + if isinstance(version, dict): + dic_version = version + # Get the version/tag + if not 'tag' in dic_version: + version = config.APPLICATION.tag + else: + version = dic_version["tag"] + + # Get the debug if any + if 'debug' in dic_version: + debug = dic_version["debug"] + elif 'debug' in config.APPLICATION: + debug = config.APPLICATION.debug + + # Get the verbose if any + if 'verbose' in dic_version: + verbose = dic_version["verbose"] + elif 'verbose' in config.APPLICATION: + verbose = config.APPLICATION.verbose + + # Get the dev if any + if 'dev' in dic_version: + dev = dic_version["dev"] + elif 'dev' in config.APPLICATION: + dev = config.APPLICATION.dev + + # Get the hpc if any + if 'hpc' in dic_version: + hpc = dic_version.hpc + elif 'hpc' in config.APPLICATION: + hpc = config.APPLICATION.hpc + + # Get the base if any + if 'base' in dic_version: + base = dic_version["base"] + + # Get the section if any + if 'section' in dic_version: + section = dic_version['section'] + vv = version # substitute some character with _ in order to get the correct definition # in config.PRODUCTS. This is done because the pyconf tool does not handle # the . and - characters - for c in ".-": vv = vv.replace(c, "_") - + for c in ".-/": vv = vv.replace(c, "_") + prod_info = None if product_name in config.PRODUCTS: # Search for the product description in the configuration prod_info = get_product_section(config, product_name, vv, section) + # get salomeTool version + prod_info.sat_version = src.get_salometool_version(config) + # merge opt_depend in depend if prod_info is not None and 'opt_depend' in prod_info: for depend in prod_info.opt_depend: 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: @@ -109,9 +197,14 @@ def get_product_config(config, product_name, with_install_dir=True): # In case of a fixed product, # define the install_dir (equal to the version) - if prod_info is not None and os.path.isdir(version): + if prod_info is not None and \ + (os.path.isdir(version) or version.startswith("/")): + # we consider a (fixed) path existing paths; + # or paths starting with '/' (the objective is to print a correct + # message to the user in case of non existing path.) prod_info.install_dir = version prod_info.get_source = "fixed" + prod_info.install_mode = "fixed" # Check if the product is defined as native in the application if prod_info is not None: @@ -145,96 +238,101 @@ def get_product_config(config, product_name, with_install_dir=True): prod_pyconf_path = src.find_file_in_lpath(product_name + ".pyconf", config.PATHS.PRODUCTPATH) if not prod_pyconf_path: - msg = _("No definition found for the product %s\n" - "Please create a %s.pyconf file." % (product_name, product_name)) + msg = _("""\ +No definition found for the product %(1)s. +Please create a %(1)s.pyconf file somewhere in: + %(2)s""") % { + "1": product_name, + "2": PP.pformat(config.PATHS.PRODUCTPATH) } else: - msg = _("No definition corresponding to the version %(version)s was" - " found in the file %(prod_file_path)s.\nPlease add a " - "section in it." % {"version" : vv, - "prod_file_path" : prod_pyconf_path} ) + msg = _("""\ +No definition corresponding to the version %(1)s was found in the file: + %(2)s. +Please add a section in it.""") % {"1" : vv, "2" : prod_pyconf_path} raise src.SatException(msg) # Set the debug, dev and version keys prod_info.debug = debug + prod_info.verbose = verbose 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": if not "archive_info" in prod_info: prod_info.addMapping("archive_info", src.pyconf.Mapping(prod_info), "") - if "archive_name" not in prod_info.archive_info: + 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" - arch_path = src.find_file_in_lpath(arch_name, - config.PATHS.ARCHIVEPATH) - if not arch_path: - msg = _("Archive %(arch_name)s for %(prod_name)s not found:" - "\n" % {"arch_name" : arch_name, - "prod_name" : prod_info.name}) - raise src.SatException(msg) - prod_info.archive_info.archive_name = arch_path + + arch_path = src.find_file_in_lpath(arch_name, + config.PATHS.ARCHIVEPATH) + 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 + prod_info.archive_info.archive_name = arch_name #without path else: - if (os.path.basename(prod_info.archive_info.archive_name) == - prod_info.archive_info.archive_name): - arch_name = prod_info.archive_info.archive_name - arch_path = src.find_file_in_lpath( - arch_name, - config.PATHS.ARCHIVEPATH) - if not arch_path: - msg = _("Archive %(arch_name)s for %(prod_name)s not found:" - "\n" % {"arch_name" : arch_name, - "prod_name" : prod_info.name}) - raise src.SatException(msg) - prod_info.archive_info.archive_name = arch_path + prod_info.archive_info.archive_name = arch_path + # If the product compiles with a script, check the script existence # and if it is executable if product_has_script(prod_info): # Check the compil_script key existence if "compil_script" not in prod_info: - msg = _("No compilation script found for the product %s\n" - "Please provide a \"compil_script\" key in its definition." - % (product_name)) + msg = _("""\ +No compilation script found for the product %s. +Please provide a 'compil_script' key in its definition.""") % product_name raise src.SatException(msg) - # Get the path of the script + # Get the path of the script file + # if windows supposed '.bat', if linux supposed '.sh' + # but user set extension script file in his pyconf as he wants, no obligation. script = prod_info.compil_script script_name = os.path.basename(script) if script == script_name: # Only a name is given. Search in the default directory - script_path = src.find_file_in_lpath(script_name, - config.PATHS.PRODUCTPATH, - "compil_scripts") + script_path = src.find_file_in_lpath(script_name, config.PATHS.PRODUCTPATH, "compil_scripts") if not script_path: - raise src.SatException(_("Compilation script not found: %s") % - script_name) + msg = _("Compilation script %s not found in") % script_name + DBG.tofix(msg, config.PATHS.PRODUCTPATH, True) # say where searched + script_path = "%s_(Not_Found_by_Sat!!)" % script_name prod_info.compil_script = script_path + # Check that the script is executable - if not os.access(prod_info.compil_script, os.X_OK): - raise src.SatException( - _("Compilation script cannot be executed: %s") % - prod_info.compil_script) + if os.path.exists(prod_info.compil_script) and not os.access(prod_info.compil_script, os.X_OK): + DBG.tofix("Compilation script file is not in 'execute mode'", prod_info.compil_script, True) # Get the full paths of all the patches if product_has_patches(prod_info): patches = [] - for patch in prod_info.patches: - patch_path = patch - # If only a filename, then search for the patch in the PRODUCTPATH - if os.path.basename(patch_path) == patch_path: - # Search in the PRODUCTPATH/patches - patch_path = src.find_file_in_lpath(patch, - config.PATHS.PRODUCTPATH, - "patches") - if not patch_path: - msg = _("Patch %(patch_name)s for %(prod_name)s not found:" - "\n" % {"patch_name" : patch, - "prod_name" : prod_info.name}) - raise src.SatException(msg) - patches.append(patch_path) + try: + for patch in prod_info.patches: + patch_path = patch + # If only a filename, then search for the patch in the PRODUCTPATH + if os.path.basename(patch_path) == patch_path: + # Search in the PRODUCTPATH/patches + patch_path = src.find_file_in_lpath(patch, + config.PATHS.PRODUCTPATH, + "patches") + if not patch_path: + msg = _("Patch %(patch_name)s for %(prod_name)s not found:" + "\n" % {"patch_name" : patch, + "prod_name" : prod_info.name}) + raise src.SatException(msg) + patches.append(patch_path) + except: + DBG.tofix("problem in prod_info.patches", prod_info) prod_info.patches = patches # Get the full paths of the environment scripts @@ -260,111 +358,232 @@ def get_product_config(config, product_name, with_install_dir=True): # The variable with_install_dir is at false only for internal use # of the function get_install_dir + # Save the install_dir key if there is any + if "install_dir" in prod_info and not "install_dir_save" in prod_info: + prod_info.install_dir_save = prod_info.install_dir + + # if it is not the first time the install_dir is computed, it means + # that install_dir_save exists and it has to be taken into account. + if "install_dir_save" in prod_info: + 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,prod_info.install_mode = get_install_dir(config, version, prod_info) return prod_info def get_product_section(config, product_name, version, section=None): - '''Get the product description from the configuration + """Build the product description from the configuration :param config Config: The global configuration :param product_name str: The product name - :param version str: The version of the product + :param version str: The version of the product as 'V8_4_0', or else. :param section str: The searched section (if not None, the section is explicitly given :return: The product description :rtype: Config - ''' + """ + + + #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 - # if section is not None, try to get the corresponding section + # decode version number + try: + versionMMP = VMMP.MinorMajorPatch(version) + except: # example setuptools raise "minor in major_minor_patch is not integer: '0_6c11'" + versionMMP = None + + # if a section is explicitely specified we select it if section: - if section not in config.PRODUCTS[product_name]: - return None + if section not in aProd: + pi=None # returns specific information for the given version - prod_info = config.PRODUCTS[product_name][section] - prod_info.section = section - 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 - if "version_" + version in config.PRODUCTS[product_name]: + # ex: 'version_V6_6_0' as salome version classical syntax + elif "version_" + version in aProd: # returns specific information for the given version - prod_info = config.PRODUCTS[product_name]["version_" + version] - prod_info.section = "version_" + version - 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_name = config.PRODUCTS[product_name].keys() - l_section_ranges = [section_name for section_name in l_section_name - if VERSION_DELIMITER in section_name] - for section_range in l_section_ranges: - minimum, maximum = section_range.split(VERSION_DELIMITER) - if (src.only_numbers(version) >= src.only_numbers(minimum) - and src.only_numbers(version) <= src.only_numbers(maximum)): - # returns specific information for the versions - prod_info = config.PRODUCTS[product_name][section_range] - prod_info.section = section_range - return prod_info - - # Else, get the standard informations - if "default" in config.PRODUCTS[product_name]: - # returns the generic information (given version not found) - prod_info = config.PRODUCTS[product_name].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 prod_info.section = "default" - return prod_info - - # if noting was found, return None - return None + 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): - '''Compute the installation directory of a given product +def get_install_dir(config, version, prod_info): + """Compute the installation directory of a given product :param config Config: The global configuration :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 + user wants the product to be in base. If "no", he wants the + product to be in the application workdir :param version str: The version of the product :param product_info Config: The configuration specific to the product - :return: The path of the product installation - :rtype: str - ''' + :return: The path of the product installation and the mode of the install directory (base/implicit/fixed/value) + :rtype: str,str + """ 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 base, 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") # product is declared as base in its own config + or ("base" in prod_info and prod_info.base != "no") ): # product is declared as base in the application + # 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 - and config.APPLICATION.no_base == "yes")): + + # check desactivation of base at application level + if ( "base" in prod_info and prod_info.base == "no"): in_base = False - + if in_base: install_dir = get_base_install_dir(config, prod_info, version) + install_mode = "base" else: - if "install_dir" not in prod_info or prod_info.install_dir == "base": + if ("install_mode" in prod_info and prod_info.install_mode in ["implicit", "base"]) or\ + ("install_dir" not in prod_info or prod_info.install_dir == "base"): + # the check to "base" comes from the package case were base mode is changed dynamically + # to create a package launcher. + # Set it to the default value (in application directory) - install_dir = os.path.join(config.APPLICATION.workdir, - "INSTALL", - prod_info.name) + install_mode = "implicit" + 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 + install_mode = "value" - return install_dir + return install_dir,install_mode def get_base_install_dir(config, prod_info, version): - '''Compute the installation directory of a product in base + """Compute the installation directory of a product in base :param config Config: The global configuration :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 - ''' + """ + + # get rid of / to avoid create subdirectories cf sat #18546 + version_wslash=version.replace("/", "_") + + 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) - prod_dir = os.path.join(base_path, prod_info.name + "-" + version) + 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_wslash) + return prod_dir + + prod_dir = os.path.join(base_path, prod_info.name + "-" + version_wslash) if not os.path.exists(prod_dir): return os.path.join(prod_dir, "config-1") @@ -385,10 +604,55 @@ def get_base_install_dir(config, prod_info, version): return install_dir -def check_config_exists(config, prod_dir, prod_info): - '''Verify that the installation directory of a product in a base exists - Check all the config- directory and verify the sat-config.pyconf file - that is in it +def add_compile_config_file(p_info, config): + '''Execute the proper configuration command(s) + in the product build directory. + + :param p_info Config: The specific config of the product + :param config Config: The global configuration + ''' + # Create the compile config + # DBG.write("add_compile_config_file", p_info, True) + res = src.pyconf.Config() + res.addMapping(p_info.name, src.pyconf.Mapping(res), "") + res[p_info.name]= p_info.version + + depprod=[] + for d in p_info.depend: + depprod.append(d) + if "build_depend" in p_info: + for d in p_info.build_depend: + depprod.append(d) + for prod_name in depprod: + if prod_name not in res: + res.addMapping(prod_name, src.pyconf.Mapping(res), "") + prod_dep_info = src.product.get_product_config(config, prod_name, False) + res[prod_name] = prod_dep_info.version + # Write it in the install directory of the product + # This file is for automatic reading/checking + # see check_config_exists method + 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 + 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: + # 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) + + +def check_config_exists(config, prod_dir, prod_info, verbose=False): + """\ + Verify that the installation directory of a product in a base exists. + Check all the config-/sat-config.py files found for correspondence + with current config and prod_info depend-version-tags :param config Config: The global configuration :param prod_dir str: The product installation directory path @@ -398,33 +662,43 @@ def check_config_exists(config, prod_dir, prod_info): :return: True or false is the installation is found or not and if it is found, the path of the found installation :rtype: (boolean, str) - ''' - # check if the directories or files of the directory corresponds to the + """ + # check if the directories or files of the directory corresponds to the # directory installation of the product - l_dir_and_files = os.listdir(prod_dir) + if os.path.isdir(prod_dir): + l_dir_and_files = os.listdir(prod_dir) + else: + raise Exception("Inexisting directory '%s'" % prod_dir) + + DBG.write("check_config_exists 000", (prod_dir, l_dir_and_files), verbose) + DBG.write("check_config_exists 111", prod_info, verbose) + + depend_all=[] + if "depend" in prod_info: + for d in prod_info.depend: + depend_all.append(d) + if "build_depend" in prod_info: + for d in prod_info.build_depend: + depend_all.append(d) for dir_or_file in l_dir_and_files: oExpr = re.compile(config_expression) if not(oExpr.search(dir_or_file)): - # not config-, not interesting + # in mode BASE, not config-, not interesting + # DBG.write("not interesting", dir_or_file, True) 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, src.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 - # If there is no dependency, it is the right path - if len(prod_info.depend)==0: - compile_cfg = src.pyconf.Config(config_file) - if len(compile_cfg) == 0: - return True, os.path.join(prod_dir, dir_or_file) - continue - # check if there is the config described in the file corresponds the # dependencies of the product config_corresponds = True compile_cfg = src.pyconf.Config(config_file) - for prod_dep in prod_info.depend: + for prod_dep in depend_all: # if the dependency is not in the config, # the config does not correspond if prod_dep not in compile_cfg: @@ -437,28 +711,42 @@ def check_config_exists(config, prod_dir, prod_info): if prod_dep_info.version != compile_cfg[prod_dep]: config_corresponds = False break - - for prod_name in compile_cfg: - if prod_name not in prod_info.depend: + + if config_corresponds: + for prod_name in compile_cfg: + # assume new compatibility with prod_name in sat-config.pyconf files + if prod_name == prod_info.name: + if prod_info.version == compile_cfg[prod_name]: + DBG.write("check_config_exists OK 333", compile_cfg, verbose) + pass + else: # no correspondence with newer with prod_name sat-config.pyconf files + config_corresponds = False + break + else: + # as old compatibility without prod_name sat-config.pyconf files + if prod_name not in depend_all: + # here there is an unexpected depend in an old compilation config_corresponds = False break - if config_corresponds: + if config_corresponds: # returns (and stops) at first correspondence found + DBG.write("check_config_exists OK 444", dir_or_file, verbose) return True, os.path.join(prod_dir, dir_or_file) - + + # no correspondence found return False, None def get_products_infos(lproducts, config): - '''Get the specific configuration of a list of products + """Get the specific configuration of a list of products :param lproducts List: The list of product names :param config Config: The global configuration :return: the list of tuples (product name, specific configuration of the product) :rtype: [(str, Config)] - ''' + """ products_infos = [] # Loop on product names for prod in lproducts: @@ -467,50 +755,135 @@ def get_products_infos(lproducts, config): if prod_info is not None: products_infos.append((prod, prod_info)) else: - msg = _("The %s product has no definition " - "in the configuration.") % prod + msg = _("The %s product has no definition in the configuration.") % prod raise src.SatException(msg) return products_infos -def get_product_dependencies(config, product_info): - '''Get recursively the list of products that are - in the product_info dependencies + +def get_products_list(options, cfg, logger): + """ + method that gives the product list with their informations from + configuration regarding the passed options. + + :param options Options: The Options instance that stores the commands arguments + :param cfg Config: The global configuration + :param logger Logger: The logger instance to use for the display and logging + :return: The list of (product name, product_informations). + :rtype: List + """ + # Get the products to be prepared, regarding the options + if options.products is None: + # No options, get all products sources + products = cfg.APPLICATION.products + else: + # if option --products, check that all products of the command line + # are present in the application. + """products = options.products + for p in products: + if p not in cfg.APPLICATION.products: + raise src.SatException(_("Product %(product)s " + "not defined in application %(application)s") % + { 'product': p, 'application': cfg.VARS.application} )""" + + products = src.getProductNames(cfg, options.products, logger) + + # Construct the list of tuple containing + # the products name and their definition + resAll = src.product.get_products_infos(products, cfg) + + # if the property option was passed, filter the list + if options.properties: # existing properties + ok = [] + ko = [] + res =[] + prop, value = options.properties # for example 'is_SALOME_module', 'yes' + for p_name, p_info in resAll: + try: + if p_info.properties[prop] == value: + res.append((p_name, p_info)) + ok.append(p_name) + else: + ko.append(p_name) + except: + 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" % + (options.properties, PP.pformat(sorted(ok)), PP.pformat(sorted(ko)))) + else: + logger.warning("properties %s\n seems useless with no products rejected" % + (options.properties)) + else: + res = resAll # not existing properties as all accepted + + return res + + +def get_product_dependencies(config, product_name, product_info): + """\ + Get the list of products that are + in the product_info dependencies :param config Config: The global configuration :param product_info Config: The configuration specific to the product :return: the list of products in dependence :rtype: list - ''' - if "depend" not in product_info or product_info.depend == []: - return [] - res = [] - for prod in product_info.depend: - if prod == product_info.name: - continue - if prod not in res: - res.append(prod) - prod_info = get_product_config(config, prod) - dep_prod = get_product_dependencies(config, prod_info) - for prod_in_dep in dep_prod: - if prod_in_dep not in res: - res.append(prod_in_dep) - return res + """ + from compile import get_dependencies_graph, depth_search_graph + all_products_infos = get_products_infos( + config.APPLICATION.products, + config) + all_products_graph=get_dependencies_graph(all_products_infos) + res=[] + res=depth_search_graph(all_products_graph, product_name, res) + return res[1:] # remove the product himself (in first position) -def check_installation(product_info): - '''Verify if a product is well installed. Checks install directory presence - and some additional files if it is defined in the config +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 :param product_info Config: The configuration specific to the product :return: True if it is well installed :rtype: boolean - ''' + """ + # don't check products that are not compiled if not product_compiles(product_info): return True + + if product_is_native(product_info): + # check a system product + check_cmd=src.system.get_pkg_check_cmd(config.VARS.dist_name) + run_pkg,build_pkg=src.product.check_system_dep(config.VARS.dist, check_cmd, product_info) + build_dep_ko=[] + for pkg in build_pkg: + if "KO" in build_pkg[pkg]: + build_dep_ko.append(pkg) + if build_dep_ko: + # the product is not installed : display message and return error status + msg="Please install them with %s before compiling salome" % check_cmd[0] + print("\nmissing compile time dependencies : ") + for md in build_dep_ko: + print(md) + print(msg) + return False + else: + return True + install_dir = product_info.install_dir - if not os.path.exists(install_dir): - return False + if src.product.product_is_fixed(product_info): + # we check directly the install dir only for fixed products + # (there is no pyconf file in that case) + if not os.path.exists(install_dir): + return False + else: + filename = CONFIG_FILENAME + product_info.name + ".pyconf" + if not os.path.exists(os.path.join(install_dir, filename)): + 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: @@ -519,167 +892,185 @@ def check_installation(product_info): return False return True -def product_is_sample(product_info): - '''Know if a product has the sample type +def check_source(product_info): + """Verify if a sources of product is preset. Checks source directory presence :param product_info Config: The configuration specific to the product - :return: True if the product has the sample type, else False + :return: True if it is well installed :rtype: boolean - ''' - if 'type' in product_info: - ptype = product_info.type - return ptype.lower() == 'sample' - else: + """ + source_dir = product_info.source_dir + if not os.path.exists(source_dir): return False + if ("present_files" in product_info and + "source" in product_info.present_files): + for file_relative_path in product_info.present_files.source: + file_path = os.path.join(source_dir, file_relative_path) + if not os.path.exists(file_path): + return False + return True def product_is_salome(product_info): - '''Know if a product is of type salome + """Know if a product is a SALOME module :param product_info Config: The configuration specific to the product - :return: True if the product is salome, else False + :return: True if the product is a SALOME module, else False :rtype: boolean - ''' - if 'type' in product_info: - ptype = product_info.type - return ptype.lower() == 'salome' - else: - return False + """ + return ("properties" in product_info and + "is_SALOME_module" in product_info.properties and + product_info.properties.is_SALOME_module == "yes") def product_is_fixed(product_info): - '''Know if a product is fixed + """Know if a product is fixed :param product_info Config: The configuration specific to the product :return: True if the product is fixed, else False :rtype: boolean - ''' + """ get_src = product_info.get_source return get_src.lower() == 'fixed' def product_is_native(product_info): - '''Know if a product is native + """Know if a product is native :param product_info Config: The configuration specific to the product :return: True if the product is native, else False :rtype: boolean - ''' + """ get_src = product_info.get_source return get_src.lower() == 'native' def product_is_dev(product_info): - '''Know if a product is in dev mode + """Know if a product is in dev mode :param product_info Config: The configuration specific to the product :return: True if the product is in dev mode, else False :rtype: boolean - ''' + """ dev = product_info.dev - return dev.lower() == 'yes' + res = (dev.lower() == 'yes') + DBG.write('product_is_dev %s' % product_info.name, res) + # if product_info.name == "XDATA": return True #test #10569 + return res + +def product_is_hpc(product_info): + """Know if a product is in hpc mode + + :param product_info Config: The configuration specific to + the product + :return: True if the product is in hpc mode, else False + :rtype: boolean + """ + hpc = product_info.hpc + res = (hpc.lower() == 'yes') + return res def product_is_debug(product_info): - '''Know if a product is in debug mode + """Know if a product is in debug mode :param product_info Config: The configuration specific to the product :return: True if the product is in debug mode, else False :rtype: boolean - ''' + """ debug = product_info.debug return debug.lower() == 'yes' +def product_is_verbose(product_info): + """Know if a product is in verbose mode + + :param product_info Config: The configuration specific to + the product + :return: True if the product is in verbose mode, else False + :rtype: boolean + """ + verbose = product_info.verbose + return verbose.lower() == 'yes' + def product_is_autotools(product_info): - '''Know if a product is compiled using the autotools + """Know if a product is compiled using the autotools :param product_info Config: The configuration specific to the product :return: True if the product is autotools, else False :rtype: boolean - ''' + """ build_src = product_info.build_source return build_src.lower() == 'autotools' def product_is_cmake(product_info): - '''Know if a product is compiled using the cmake + """Know if a product is compiled using the cmake :param product_info Config: The configuration specific to the product :return: True if the product is cmake, else False :rtype: boolean - ''' + """ build_src = product_info.build_source return build_src.lower() == 'cmake' def product_is_vcs(product_info): - '''Know if a product is download using git, svn or cvs (not archive) + """Know if a product is download using git, svn or cvs (not archive) :param product_info Config: The configuration specific to the product :return: True if the product is vcs, else False :rtype: boolean - ''' + """ return product_info.get_source in AVAILABLE_VCS -def product_is_SALOME(product_info): - '''Know if a product is a SALOME module - - :param product_info Config: The configuration specific to - the product - :return: True if the product is a SALOME module, else False - :rtype: boolean - ''' - return ("properties" in product_info and - "is_SALOME_module" in product_info.properties and - product_info.properties.is_SALOME_module == "yes") - def product_is_smesh_plugin(product_info): - '''Know if a product is a SMESH plugin + """Know if a product is a SMESH plugin :param product_info Config: The configuration specific to the product :return: True if the product is a SMESH plugin, else False :rtype: boolean - ''' + """ return ("properties" in product_info and "smesh_plugin" in product_info.properties and product_info.properties.smesh_plugin == "yes") def product_is_cpp(product_info): - '''Know if a product is cpp + """Know if a product is cpp :param product_info Config: The configuration specific to the product :return: True if the product is a cpp, else False :rtype: boolean - ''' + """ return ("properties" in product_info and "cpp" in product_info.properties and product_info.properties.cpp == "yes") def product_compiles(product_info): - '''Know if a product compiles or not (some products do not have a - compilation procedure) + """\ + Know if a product compiles or not + (some products do not have a compilation procedure) :param product_info Config: The configuration specific to the product :return: True if the product compiles, else False :rtype: boolean - ''' + """ return not("properties" in product_info and "compilation" in product_info.properties and product_info.properties.compilation == "no") def product_has_script(product_info): - '''Know if a product has a compilation script + """Know if a product has a compilation script :param product_info Config: The configuration specific to the product :return: True if the product it has a compilation script, else False :rtype: boolean - ''' + """ if "build_source" not in product_info: # Native case return False @@ -687,82 +1078,208 @@ def product_has_script(product_info): return build_src.lower() == 'script' def product_has_env_script(product_info): - '''Know if a product has an environment script + """Know if a product has an environment script :param product_info Config: The configuration specific to the product :return: True if the product it has an environment script, else False :rtype: boolean - ''' + """ return "environ" in product_info and "env_script" in product_info.environ def product_has_patches(product_info): - '''Know if a product has one or more patches + """Know if a product has one or more patches :param product_info Config: The configuration specific to the product :return: True if the product has one or more patches :rtype: boolean - ''' - return "patches" in product_info and len(product_info.patches) > 0 + """ + res = ( "patches" in product_info and len(product_info.patches) > 0 ) + return res def product_has_logo(product_info): - '''Know if a product has a logo (YACSGEN generate) + """Know if a product has a logo (YACSGEN generate) :param product_info Config: The configuration specific to the product :return: The path of the logo if the product has a logo, else False :rtype: Str - ''' + """ if ("properties" in product_info and "logo" in product_info.properties): return product_info.properties.logo else: return False +def product_has_licence(product_info, path): + """Find out if a product has a licence + + :param product_info Config: The configuration specific to the product + :param path Str: The path where to search for the licence + :return: The name of the licence file (the complete path if it is found in the path, else the name, else False + :rtype: Str + """ + if ("properties" in product_info and + "licence" in product_info.properties): + licence_name = product_info.properties.licence + if len(path) > 0: + # search for licence_name in path + # a- consolidate the path into one signe string licence_path + licence_path=path[0] + for lpath in path[1:]: + licence_path=licence_path+":"+lpath + licence_path_list=licence_path.split(":") + licence_fullname = src.find_file_in_lpath(licence_name, licence_path_list) + if licence_fullname: + return licence_fullname + + # if the search of licence in path failed, we return its name (not the full path) + return licence_name + + else: + return False # product has no licence + def product_has_salome_gui(product_info): - '''Know if a product has a SALOME gui + """Know if a product has a SALOME gui :param product_info Config: The configuration specific to the product :return: True if the product has a SALOME gui, else False :rtype: Boolean - ''' + """ return ("properties" in product_info and "has_salome_gui" in product_info.properties and product_info.properties.has_salome_gui == "yes") def product_is_mpi(product_info): - '''Know if a product has openmpi in its dependencies + """Know if a product has openmpi in its dependencies :param product_info Config: The configuration specific to the product :return: True if the product has openmpi inits dependencies :rtype: boolean - ''' + """ return "openmpi" in product_info.depend def product_is_generated(product_info): - '''Know if a product is generated (YACSGEN) + """Know if a product is generated (YACSGEN) :param product_info Config: The configuration specific to the product :return: True if the product is generated :rtype: boolean - ''' + """ return ("properties" in product_info and "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_is_compile_and_runtime(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_and_runtime" in product_info.properties and + product_info.properties.compile_and_runtime == "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 check_system_dep(distrib, check_cmd, product_info): + """Search for system dependencies, check if installed + :param dist : The linux ditribution (CO7,DB10...) + :param check_cmd Config: The command to use for checking (rpm/apt) + :param product_info Config: The configuration specific to the product + :rtype: two dictionnaries for runtime and compile time dependencies with text status + """ + runtime_dep={} + build_dep={} + + if "system_info" in product_info: + + sysinfo=product_info.system_info + additional_sysinfo = None + + for key in sysinfo : + if distrib in key : + additional_sysinfo = sysinfo[key] + + if check_cmd[0]=="rpm": + if "rpm" in sysinfo: + for pkg in sysinfo.rpm: + runtime_dep[pkg]=src.system.check_system_pkg(check_cmd,pkg) + if "rpm_dev" in sysinfo: + for pkg in sysinfo.rpm_dev: + build_dep[pkg]=src.system.check_system_pkg(check_cmd,pkg) + if additional_sysinfo : + if "rpm" in additional_sysinfo: + for pkg in additional_sysinfo.rpm: + runtime_dep[pkg]=src.system.check_system_pkg(check_cmd,pkg) + if "rpm_dev" in additional_sysinfo: + for pkg in additional_sysinfo.rpm_dev: + build_dep[pkg]=src.system.check_system_pkg(check_cmd,pkg) + if check_cmd[0]=="apt": + if "apt" in sysinfo: + for pkg in sysinfo.apt: + runtime_dep[pkg]=src.system.check_system_pkg(check_cmd,pkg) + if "apt_dev" in sysinfo: + for pkg in sysinfo.apt_dev: + build_dep[pkg]=src.system.check_system_pkg(check_cmd,pkg) + if additional_sysinfo : + if "apt" in additional_sysinfo: + for pkg in additional_sysinfo.apt: + runtime_dep[pkg]=src.system.check_system_pkg(check_cmd,pkg) + if "apt_dev" in additional_sysinfo: + for pkg in additional_sysinfo.apt_dev: + build_dep[pkg]=src.system.check_system_pkg(check_cmd,pkg) + + return runtime_dep,build_dep + + def get_product_components(product_info): - '''Get the component list to generate with the product + """Get the component list to generate with the product :param product_info Config: The configuration specific to the product :return: The list of names of the components :rtype: List - ''' + """ if not product_is_generated(product_info): return [] @@ -773,4 +1290,16 @@ def get_product_components(product_info): if isinstance(compo_list, str): compo_list = [ compo_list ] - return compo_list \ No newline at end of file + 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") +