X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2Fproduct.py;h=4c72a43657f385fd5729358b2ff00416a0d74032;hb=28cebd157f9d39920d0480232e7c361716ca45bb;hp=f6c8cc214b4c52fdcfa4cfb3a8de5843ff4813fb;hpb=cfea4f7ad71439f4b1eac0801602e5ae422b880c;p=tools%2Fsat.git diff --git a/src/product.py b/src/product.py index f6c8cc2..4c72a43 100644 --- a/src/product.py +++ b/src/product.py @@ -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): @@ -113,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: @@ -163,7 +165,7 @@ def get_product_config(config, product_name, with_install_dir=True): # 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: @@ -202,6 +204,7 @@ def get_product_config(config, product_name, with_install_dir=True): # 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: @@ -254,6 +257,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": @@ -274,9 +279,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 @@ -366,12 +368,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,prod_info.install_mode = 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 @@ -382,76 +384,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,34 +494,60 @@ def get_install_dir(config, base, version, prod_info): :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 - # base : corresponds to what is specified in application pyconf (either from the global key, or from a product dict) + + # 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") - or base == "yes"): + 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 - # what was declared in application has precedence over what was said in product pyconf - # no_base="yes" has precedence over base == "yes" - 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 @@ -499,11 +556,34 @@ 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 """ + + # 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") @@ -537,7 +617,13 @@ def add_compile_config_file(p_info, config): res.addMapping(p_info.name, src.pyconf.Mapping(res), "") res[p_info.name]= p_info.version - for prod_name in p_info.depend: + 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) @@ -545,12 +631,14 @@ 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 @@ -585,6 +673,13 @@ def check_config_exists(config, prod_dir, prod_info, verbose=False): 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)): @@ -593,7 +688,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 @@ -602,7 +698,7 @@ def check_config_exists(config, prod_dir, prod_info, verbose=False): # 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: @@ -628,7 +724,7 @@ def check_config_exists(config, prod_dir, prod_info, verbose=False): break else: # as old compatibility without prod_name sat-config.pyconf files - if prod_name not in prod_info.depend: + if prod_name not in depend_all: # here there is an unexpected depend in an old compilation config_corresponds = False break @@ -723,9 +819,9 @@ def get_products_list(options, cfg, logger): return res -def get_product_dependencies(config, product_info): +def get_product_dependencies(config, product_name, product_info): """\ - Get recursively the list of products that are + Get the list of products that are in the product_info dependencies :param config Config: The global configuration @@ -734,22 +830,16 @@ def get_product_dependencies(config, product_info): :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 - -def check_installation(product_info): + 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(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,11 +849,41 @@ def check_installation(product_info): :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: @@ -780,7 +900,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 @@ -977,8 +1096,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): @@ -1069,6 +1186,91 @@ def product_is_compile_time(product_info): "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