Salome HOME
Merge branch 'nct/may21'
[tools/sat.git] / commands / package.py
index c24de6ebc95ab0582f4fd8d119ae52bb5ef7f291..5cc26e6933f176bd40c44781e3ec4489b190f713 100644 (file)
@@ -25,12 +25,14 @@ import codecs
 import string
 import glob
 import pprint as PP
-
+import sys
 import src
 
 from application import get_SALOME_modules
 import src.debug as DBG
 
+old_python = sys.version_info[0] == 2 and sys.version_info[1] <= 6
+
 BINARY = "binary"
 SOURCE = "Source"
 PROJECT = "Project"
@@ -42,6 +44,8 @@ PROJECT_DIR = "PROJECT"
 IGNORED_DIRS = [".git", ".svn"]
 IGNORED_EXTENSIONS = []
 
+PACKAGE_EXT=".tar.gz" # the extension we use for the packages
+
 PROJECT_TEMPLATE = """#!/usr/bin/env python
 #-*- coding:utf-8 -*-
 
@@ -71,8 +75,8 @@ LOCAL_TEMPLATE = ("""#!/usr/bin/env python
     workdir : 'default'
     log_dir : 'default'
     archive_dir : 'default'
-    VCS : None
-    tag : None
+    VCS : 'unknown'
+    tag : 'unknown'
   }
 
 PROJECTS :
@@ -100,6 +104,8 @@ parser.add_option('', 'ftp', 'boolean', 'ftp',
     _('Optional: Do not embed archives for products in archive mode.' 
     'Sat prepare will use ftp instead to retrieve them'),
     False)
+parser.add_option('e', 'exe', 'string', 'exe',
+    _('Optional: Produce an extra launcher based upon the exe given as argument.'), "")
 parser.add_option('p', 'project', 'string', 'project',
     _('Optional: Produce an archive that contains a project.'), "")
 parser.add_option('t', 'salometools', 'boolean', 'sat',
@@ -108,8 +114,6 @@ parser.add_option('n', 'name', 'string', 'name',
     _('Optional: The name or full path of the archive.'), None)
 parser.add_option('', 'add_files', 'list2', 'add_files',
     _('Optional: The list of additional files to add to the archive.'), [])
-parser.add_option('', 'without_commercial', 'boolean', 'without_commercial',
-    _('Optional: do not add commercial licence.'), False)
 parser.add_option('', 'without_properties', 'properties', 'without_properties',
     _('Optional: Filter the products by their properties.\n\tSyntax: '
       '--without_properties <property>:<value>'))
@@ -138,6 +142,8 @@ def add_files(tar, name_archive, d_content, logger, f_exclude=None):
     names = sorted(d_content.keys())
     DBG.write("add tar names", names)
 
+    # used to avoid duplications (for pip install in python, or single_install_dir cases)
+    already_added=set() 
     for name in names:
         # display information
         len_points = max_len - len(name) + 3
@@ -148,7 +154,17 @@ def add_files(tar, name_archive, d_content, logger, f_exclude=None):
         # of the directory or file to add
         # Add it in the archive
         try:
-            tar.add(local_path, arcname=in_archive, exclude=f_exclude)
+            key=local_path+"->"+in_archive
+            if key not in already_added:
+                if old_python:
+                    tar.add(local_path,
+                                 arcname=in_archive,
+                                 exclude=exclude_VCS_and_extensions_26)
+                else:
+                    tar.add(local_path,
+                                 arcname=in_archive,
+                                 filter=exclude_VCS_and_extensions)
+                already_added.add(key)
             logger.write(src.printcolors.printcSuccess(_("OK")), 3)
         except Exception as e:
             logger.write(src.printcolors.printcError(_("KO ")), 3)
@@ -157,9 +173,10 @@ def add_files(tar, name_archive, d_content, logger, f_exclude=None):
         logger.write("\n", 3)
     return success
 
-def exclude_VCS_and_extensions(filename):
+
+def exclude_VCS_and_extensions_26(filename):
     ''' The function that is used to exclude from package the link to the 
-        VCS repositories (like .git)
+        VCS repositories (like .git) (only for python 2.6)
 
     :param filename Str: The filname to exclude (or not).
     :return: True if the file has to be exclude
@@ -173,12 +190,28 @@ def exclude_VCS_and_extensions(filename):
             return True
     return False
 
+def exclude_VCS_and_extensions(tarinfo):
+    ''' The function that is used to exclude from package the link to the 
+        VCS repositories (like .git)
+
+    :param filename Str: The filname to exclude (or not).
+    :return: None if the file has to be exclude
+    :rtype: tarinfo or None
+    '''
+    filename = tarinfo.name
+    for dir_name in IGNORED_DIRS:
+        if dir_name in filename:
+            return None
+    for extension in IGNORED_EXTENSIONS:
+        if filename.endswith(extension):
+            return None
+    return tarinfo
+
 def produce_relative_launcher(config,
                               logger,
                               file_dir,
                               file_name,
-                              binaries_dir_name,
-                              with_commercial=True):
+                              binaries_dir_name):
     '''Create a specific SALOME launcher for the binary package. This launcher 
        uses relative paths.
     
@@ -192,8 +225,21 @@ def produce_relative_launcher(config,
     :rtype: str
     '''
     
+    # set base mode to "no" for the archive - save current mode to restore it at the end
+    if "base" in config.APPLICATION:
+        base_setting=config.APPLICATION.base 
+    else:
+        base_setting="maybe"
+    config.APPLICATION.base="no"
+
     # get KERNEL installation path 
-    kernel_root_dir = os.path.join(binaries_dir_name, "KERNEL")
+    kernel_info = src.product.get_product_config(config, "KERNEL")
+    kernel_base_name=os.path.basename(kernel_info.install_dir)
+    if kernel_info.install_mode == "base":
+        # case of kernel installed in base. the kernel install dir name is different in the archive
+        kernel_base_name=os.path.basename(os.path.dirname(kernel_info.install_dir))
+    
+    kernel_root_dir = os.path.join(binaries_dir_name, kernel_base_name)
 
     # set kernel bin dir (considering fhs property)
     kernel_cfg = src.product.get_product_config(config, "KERNEL")
@@ -223,40 +269,35 @@ def produce_relative_launcher(config,
     else:
         app_root_dir=os.path.join(binaries_dir_name, salome_application_name)
 
-    # Get the launcher template and do substitutions
+    additional_env={}
+    additional_env['sat_bin_kernel_install_dir'] = "out_dir_Path + " +\
+                                                   config.VARS.sep + bin_kernel_install_dir
     if "python3" in config.APPLICATION and config.APPLICATION.python3 == "yes":
-        withProfile = src.fileEnviron.withProfile3
+        additional_env['sat_python_version'] = 3
     else:
-        withProfile = src.fileEnviron.withProfile
-
-    withProfile = withProfile.replace(
-        "ABSOLUTE_APPLI_PATH'] = 'KERNEL_INSTALL_DIR'",
-        "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + app_root_dir + "'")
-    withProfile = withProfile.replace(
-        " 'BIN_KERNEL_INSTALL_DIR'",
-        " out_dir_Path + '" + config.VARS.sep + bin_kernel_install_dir + "'")
+        additional_env['sat_python_version'] = 2
 
-    before, after = withProfile.split("# here your local standalone environment\n")
+    additional_env['ABSOLUTE_APPLI_PATH'] = "out_dir_Path" + config.VARS.sep + app_root_dir
 
     # create an environment file writer
     writer = src.environment.FileEnvWriter(config,
                                            logger,
                                            file_dir,
-                                           src_root=None)
+                                           src_root=None,
+                                           env_info=None)
     
     filepath = os.path.join(file_dir, file_name)
-    # open the file and write into it
-    launch_file = open(filepath, "w")
-    launch_file.write(before)
     # Write
-    writer.write_cfgForPy_file(launch_file,
-                               for_package = binaries_dir_name,
-                               with_commercial=with_commercial)
-    launch_file.write(after)
-    launch_file.close()
+    writer.write_env_file(filepath,
+                          False,  # for launch
+                          "cfgForPy",
+                          additional_env=additional_env,
+                          no_path_init="False",
+                          for_package = binaries_dir_name)
     
     # Little hack to put out_dir_Path outside the strings
     src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
+    src.replace_in_file(filepath, "r'out_dir_Path + ", "out_dir_Path + r'" )
     
     # A hack to put a call to a file for distene licence.
     # It does nothing to an application that has no distene product
@@ -274,6 +315,9 @@ def produce_relative_launcher(config,
              stat.S_IXGRP |
              stat.S_IXOTH)
 
+    # restore modified setting by its initial value
+    config.APPLICATION.base=base_setting
+
     return filepath
 
 def hack_for_distene_licence(filepath, licence_file):
@@ -303,7 +347,7 @@ def hack_for_distene_licence(filepath, licence_file):
     del text[num_line +1]
     del text[num_line +1]
     text_to_insert ="""    try:
-        distene_licence_file="%s"
+        distene_licence_file=r"%s"
         if sys.version_info[0] >= 3 and sys.version_info[1] >= 5:
             import importlib.util
             spec_dist = importlib.util.spec_from_file_location("distene_licence", distene_licence_file)
@@ -325,7 +369,8 @@ def hack_for_distene_licence(filepath, licence_file):
 def produce_relative_env_files(config,
                               logger,
                               file_dir,
-                              binaries_dir_name):
+                              binaries_dir_name,
+                              exe_name=None):
     '''Create some specific environment files for the binary package. These 
        files use relative paths.
     
@@ -334,9 +379,18 @@ def produce_relative_env_files(config,
     :param file_dir str: the directory where to put the files
     :param binaries_dir_name str: the name of the repository where the binaries
                                   are, in the archive.
+    :param exe_name str: if given generate a launcher executing exe_name
     :return: the list of path of the produced environment files
     :rtype: List
     '''  
+
+    # set base mode to "no" for the archive - save current mode to restore it at the end
+    if "base" in config.APPLICATION:
+        base_setting=config.APPLICATION.base 
+    else:
+        base_setting="maybe"
+    config.APPLICATION.base="no"
+
     # create an environment file writer
     writer = src.environment.FileEnvWriter(config,
                                            logger,
@@ -350,6 +404,9 @@ def produce_relative_env_files(config,
       shell = "bash"
       filename  = "env_launch.sh"
 
+    if exe_name:
+        filename=os.path.basename(exe_name)
+
     # Write
     filepath = writer.write_env_file(filename,
                           False, # for launch
@@ -357,7 +414,21 @@ def produce_relative_env_files(config,
                           for_package = binaries_dir_name)
 
     # Little hack to put out_dir_Path as environment variable
-    src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
+    if src.architecture.is_windows() :
+      src.replace_in_file(filepath, '"out_dir_Path', '"%out_dir_Path%' )
+      src.replace_in_file(filepath, '=out_dir_Path', '=%out_dir_Path%' )
+      src.replace_in_file(filepath, ';out_dir_Path', ';%out_dir_Path%' )
+    else:
+      src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
+      src.replace_in_file(filepath, ':out_dir_Path', ':${out_dir_Path}' )
+
+    if exe_name:
+        if src.architecture.is_windows():
+            cmd="\n\nrem Launch exe with user arguments\n%s " % exe_name + "%*"
+        else:
+            cmd='\n\n# Launch exe with user arguments\n%s "$*"' % exe_name
+        with open(filepath, "a") as exe_launcher:
+            exe_launcher.write(cmd)
 
     # change the rights in order to make the file executable for everybody
     os.chmod(filepath,
@@ -369,6 +440,9 @@ def produce_relative_env_files(config,
              stat.S_IXGRP |
              stat.S_IXOTH)
     
+    # restore modified setting by its initial value
+    config.APPLICATION.base=base_setting
+
     return filepath
 
 def produce_install_bin_file(config,
@@ -396,7 +470,7 @@ def produce_install_bin_file(config,
                                         "INSTALL_BIN.template")
         
         # build the name of the directory that will contain the binaries
-        binaries_dir_name = "BINARIES-" + config.VARS.dist
+        binaries_dir_name = config.INTERNAL.config.binary_dir + config.VARS.dist
         # build the substitution loop
         loop_cmd = "for f in $(grep -RIl"
         for key in d_sub:
@@ -512,6 +586,11 @@ def binary_package(config, logger, options, tmp_working_dir):
     l_products_name = sorted(config.APPLICATION.products.keys())
     l_product_info = src.product.get_products_infos(l_products_name,
                                                     config)
+
+    # suppress compile time products for binaries-only archives
+    if not options.sources:
+        update_config(config, logger, "compile_time", "yes")
+
     l_install_dir = []
     l_source_dir = []
     l_not_installed = []
@@ -523,6 +602,8 @@ def binary_package(config, logger, options, tmp_working_dir):
         config.APPLICATION.properties.mesa_launcher_in_package == "yes") :
             generate_mesa_launcher=True
 
+    # first loop on products : filter products, analyse properties,
+    # and store the information that will be used to create the archive in the second loop 
     for prod_name, prod_info in l_product_info:
         # skip product with property not_in_package set to yes
         if src.get_property_in_product_cfg(prod_info, "not_in_package") == "yes":
@@ -542,8 +623,13 @@ def binary_package(config, logger, options, tmp_working_dir):
                 or src.product.product_is_fixed(prod_info)
                 or not src.product.product_compiles(prod_info)):
             continue
+        # 
+        # products with single_dir property will be installed in the PRODUCTS directory of the archive
+        is_single_dir=(src.appli_test_property(config,"single_install_dir", "yes") and \
+                       src.product.product_test_property(prod_info,"single_install_dir", "yes"))
         if src.product.check_installation(config, prod_info):
-            l_install_dir.append((prod_name, prod_info.install_dir))
+            l_install_dir.append((prod_name, prod_info.name, prod_info.install_dir,
+                                  is_single_dir, prod_info.install_mode))
         else:
             l_not_installed.append(prod_name)
         
@@ -555,13 +641,15 @@ def binary_package(config, logger, options, tmp_working_dir):
                                            config.INTERNAL.config.install_dir,
                                            name_cpp) 
                 if os.path.exists(install_dir):
-                    l_install_dir.append((name_cpp, install_dir))
+                    l_install_dir.append((name_cpp, name_cpp, install_dir, False, "value"))
                 else:
                     l_not_installed.append(name_cpp)
         
     # check the name of the directory that (could) contains the binaries 
     # from previous detar
-    binaries_from_detar = os.path.join(config.APPLICATION.workdir, "BINARIES-" + config.VARS.dist)
+    binaries_from_detar = os.path.join(
+                              config.APPLICATION.workdir,
+                              config.INTERNAL.config.binary_dir + config.VARS.dist)
     if os.path.exists(binaries_from_detar):
          logger.write("""
 WARNING: existing binaries directory from previous detar installation:
@@ -578,13 +666,13 @@ WARNING: existing binaries directory from previous detar installation:
     if len(l_not_installed) > 0:
         text_missing_prods = ""
         for p_name in l_not_installed:
-            text_missing_prods += "-" + p_name + "\n"
+            text_missing_prods += " - " + p_name + "\n"
         if not options.force_creation:
-            msg = _("ERROR: there are missing products installations:")
+            msg = _("ERROR: there are missing product installations:")
             logger.write("%s\n%s" % (src.printcolors.printcError(msg),
                                      text_missing_prods),
                          1)
-            return None
+            raise src.SatException(msg)
         else:
             msg = _("WARNING: there are missing products installations:")
             logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
@@ -597,11 +685,11 @@ WARNING: existing binaries directory from previous detar installation:
         for p_name in l_sources_not_present:
             text_missing_prods += "-" + p_name + "\n"
         if not options.force_creation:
-            msg = _("ERROR: there are missing products sources:")
+            msg = _("ERROR: there are missing product sources:")
             logger.write("%s\n%s" % (src.printcolors.printcError(msg),
                                      text_missing_prods),
                          1)
-            return None
+            raise src.SatException(msg)
         else:
             msg = _("WARNING: there are missing products sources:")
             logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
@@ -609,13 +697,24 @@ WARNING: existing binaries directory from previous detar installation:
                          1)
  
     # construct the name of the directory that will contain the binaries
-    binaries_dir_name = "BINARIES-" + config.VARS.dist
-    
+    if src.architecture.is_windows():
+        binaries_dir_name = config.INTERNAL.config.binary_dir
+    else:
+        binaries_dir_name = config.INTERNAL.config.binary_dir + config.VARS.dist
     # construct the correlation table between the product names, there 
     # actual install directories and there install directory in archive
     d_products = {}
-    for prod_name, install_dir in l_install_dir:
-        path_in_archive = os.path.join(binaries_dir_name, prod_name)
+    for prod_name, prod_info_name, install_dir, is_single_dir, install_mode in l_install_dir:
+        prod_base_name=os.path.basename(install_dir)
+        if install_mode == "base":
+            # case of a products installed in base. 
+            # because the archive is in base:no mode, the name of the install dir is different inside archive
+            # we set it to the product name or by PRODUCTS if single-dir
+            if is_single_dir:
+                prod_base_name=config.INTERNAL.config.single_install_dir
+            else:
+                prod_base_name=prod_info_name
+        path_in_archive = os.path.join(binaries_dir_name, prod_base_name)
         d_products[prod_name + " (bin)"] = (install_dir, path_in_archive)
         
     for prod_name, source_dir in l_source_dir:
@@ -634,8 +733,7 @@ WARNING: existing binaries directory from previous detar installation:
                                                  logger,
                                                  tmp_working_dir,
                                                  launcher_name,
-                                                 binaries_dir_name,
-                                                 not(options.without_commercial))
+                                                 binaries_dir_name)
             d_products["launcher"] = (launcher_package, launcher_name)
 
             # if the application contains mesa products, we generate in addition to the 
@@ -655,8 +753,7 @@ WARNING: existing binaries directory from previous detar installation:
                                                      logger,
                                                      tmp_working_dir,
                                                      launcher_mesa_name,
-                                                     binaries_dir_name,
-                                                     not(options.without_commercial))
+                                                     binaries_dir_name)
                 d_products["launcher (mesa)"] = (launcher_package_mesa, launcher_mesa_name)
 
                 # if there was a use_mesa value, we restore it
@@ -691,6 +788,21 @@ WARNING: existing binaries directory from previous detar installation:
       filename  = "env_launch.sh"
     d_products["environment file"] = (env_file, filename)      
 
+    # If option exe, produce an extra launcher based on specified exe
+    if options.exe:
+        exe_file = produce_relative_env_files(config,
+                                              logger,
+                                              tmp_working_dir,
+                                              binaries_dir_name,
+                                              options.exe)
+            
+        if src.architecture.is_windows():
+          filename  = os.path.basename(options.exe) + ".bat"
+        else:
+          filename  = os.path.basename(options.exe) + ".sh"
+        d_products["exe file"] = (exe_file, filename)      
+    
+
     return d_products
 
 def source_package(sat, config, logger, options, tmp_working_dir):
@@ -798,7 +910,6 @@ def get_archives(config, logger):
                 pip_wheels_dir=os.path.join(config.LOCAL.archive_dir,"wheels")
                 pip_wheel_pattern=os.path.join(pip_wheels_dir, 
                     "%s-%s*" % (p_info.name, p_info.version))
-                print "CNC  pip_wheel_pattern = ",pip_wheel_pattern
                 pip_wheel_path=glob.glob(pip_wheel_pattern)
                 msg_pip_not_found="Error in get_archive, pip wheel for "\
                                   "product %s-%s was not found in %s directory"
@@ -925,12 +1036,17 @@ def make_archive(prod_name, prod_info, where):
     :return: The path of the resulting archive
     :rtype: str
     '''
-    path_targz_prod = os.path.join(where, prod_name + ".tgz")
+    path_targz_prod = os.path.join(where, prod_name + PACKAGE_EXT)
     tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
     local_path = prod_info.source_dir
-    tar_prod.add(local_path,
-                 arcname=prod_name,
-                 exclude=exclude_VCS_and_extensions)
+    if old_python:
+        tar_prod.add(local_path,
+                     arcname=prod_name,
+                     exclude=exclude_VCS_and_extensions_26)
+    else:
+        tar_prod.add(local_path,
+                     arcname=prod_name,
+                     filter=exclude_VCS_and_extensions)
     tar_prod.close()
     return path_targz_prod       
 
@@ -1013,7 +1129,10 @@ def create_project_for_src_package(config, tmp_working_dir, with_vcs, with_ftp):
                                         patches_tmp_dir,
                                         products_pyconf_tmp_dir)
     
-    find_application_pyconf(config, application_tmp_dir)
+    # for the application pyconf, we write directly the config
+    # don't search for the original pyconf file
+    # to avoid problems with overwrite sections and rm_products key
+    write_application_pyconf(config, application_tmp_dir)
     
     d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
     return d_project
@@ -1048,22 +1167,18 @@ def find_product_scripts_and_pyconf(p_name,
     '''
     
     # read the pyconf of the product
-    product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
-                                           config.PATHS.PRODUCTPATH)
-    product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
+    product_pyconf_cfg = src.pyconf.Config(p_info.from_file)
 
     # find the compilation script if any
     if src.product.product_has_script(p_info):
         compil_script_path = src.Path(p_info.compil_script)
         compil_script_path.copy(compil_scripts_tmp_dir)
-        product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
-                                                    p_info.compil_script)
+
     # find the environment script if any
     if src.product.product_has_env_script(p_info):
         env_script_path = src.Path(p_info.environ.env_script)
         env_script_path.copy(env_scripts_tmp_dir)
-        product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
-                                                p_info.environ.env_script)
+
     # find the patches if any
     if src.product.product_has_patches(p_info):
         patches = src.pyconf.Sequence()
@@ -1072,26 +1187,37 @@ def find_product_scripts_and_pyconf(p_name,
             p_path.copy(patches_tmp_dir)
             patches.append(os.path.basename(patch_path), "")
 
-        product_pyconf_cfg[p_info.section].patches = patches
-    
-    if with_vcs:
-        # put in the pyconf file the resolved values
-        for info in ["git_info", "cvs_info", "svn_info"]:
-            if info in p_info:
-                for key in p_info[info]:
-                    product_pyconf_cfg[p_info.section][info][key] = p_info[
-                                                                      info][key]
-    else:
-        # if the product is not archive, then make it become archive.
-        if src.product.product_is_vcs(p_info):
-            product_pyconf_cfg[p_info.section].get_source = "archive"
-            if not "archive_info" in product_pyconf_cfg[p_info.section]:
-                product_pyconf_cfg[p_info.section].addMapping("archive_info",
+    if (not with_vcs) and src.product.product_is_vcs(p_info):
+        # in non vcs mode, if the product is not archive, then make it become archive.
+
+        # depending upon the incremental mode, select impacted sections
+        if "properties" in p_info and "incremental" in p_info.properties and\
+            p_info.properties.incremental == "yes":
+            sections = ["default", "default_win", p_info.section, p_info.section+"_win"]
+        else:
+            sections = [p_info.section]
+        for section in sections:
+            if section in product_pyconf_cfg and "get_source" in product_pyconf_cfg[section]:
+                DBG.write("sat package set archive mode to archive for product %s and section %s" %\
+                          (p_name,section))
+                product_pyconf_cfg[section].get_source = "archive"
+                if not "archive_info" in product_pyconf_cfg[section]:
+                    product_pyconf_cfg[section].addMapping("archive_info",
                                         src.pyconf.Mapping(product_pyconf_cfg),
                                         "")
-            product_pyconf_cfg[p_info.section
-                              ].archive_info.archive_name = p_info.name + ".tgz"
+                    product_pyconf_cfg[section].archive_info.archive_name =\
+                        p_info.name + ".tgz"
     
+    if (with_vcs) and src.product.product_is_vcs(p_info):
+        # in vcs mode we must replace explicitely the git server url
+        # (or it will not be found later because project files are not exported in archives)
+        for section in product_pyconf_cfg:
+            # replace in all sections of the product pyconf the git repo definition by its substitued value (found in p_info)
+            if "git_info" in product_pyconf_cfg[section]:
+                for repo in product_pyconf_cfg[section].git_info:
+                    if repo in p_info.git_info:
+                        product_pyconf_cfg[section].git_info[repo] =  p_info.git_info[repo]
+
     # write the pyconf file to the temporary project location
     product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
                                            p_name + ".pyconf")
@@ -1100,43 +1226,35 @@ def find_product_scripts_and_pyconf(p_name,
     product_pyconf_cfg.__save__(ff, 1)
     ff.close()
 
-def find_application_pyconf(config, application_tmp_dir):
-    '''Find the application pyconf file and put it in the specific temporary 
+
+def write_application_pyconf(config, application_tmp_dir):
+    '''Write the application pyconf file in the specific temporary 
        directory containing the specific project of a source package.
 
     :param config Config: The global configuration.
     :param application_tmp_dir str: The path to the temporary application 
-                                       scripts directory of the project.
+                                    scripts directory of the project.
     '''
-    # read the pyconf of the application
     application_name = config.VARS.application
-    application_pyconf_path = src.find_file_in_lpath(
-                                            application_name + ".pyconf",
-                                            config.PATHS.APPLICATIONPATH)
-    application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
-    
-    # Change the workdir
-    application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
-                                    application_pyconf_cfg,
-                                    src.pyconf.DOLLAR,
-                                    'VARS.salometoolsway + $VARS.sep + ".."')
-
-    # Prevent from compilation in base
-    application_pyconf_cfg.APPLICATION.no_base = "yes"
-    
-    #remove products that are not in config (which were filtered by --without_properties)
-    for product_name in application_pyconf_cfg.APPLICATION.products.keys():
-        if product_name not in config.APPLICATION.products.keys():
-            application_pyconf_cfg.APPLICATION.products.__delitem__(product_name)
-
     # write the pyconf file to the temporary application location
     application_tmp_pyconf_path = os.path.join(application_tmp_dir,
                                                application_name + ".pyconf")
-
-    ff = open(application_tmp_pyconf_path, 'w')
-    ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
-    application_pyconf_cfg.__save__(ff, 1)
-    ff.close()
+    with open(application_tmp_pyconf_path, 'w') as f:
+        f.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
+        res = src.pyconf.Config()
+        app = src.pyconf.deepCopyMapping(config.APPLICATION)
+
+        # set base mode to "no" for the archive
+        app.base = "no"
+
+        # Change the workdir
+        app.workdir = src.pyconf.Reference(
+                                 app,
+                                 src.pyconf.DOLLAR,
+                                 'VARS.salometoolsway + $VARS.sep + ".."')
+        res.addMapping("APPLICATION", app, "")
+        res.__save__(f, evaluated=False)
+    
 
 def sat_package(config, tmp_working_dir, options, logger):
     '''Prepare a dictionary that stores all the needed directories and files to
@@ -1272,6 +1390,8 @@ In the following, $$ROOT represents the directory where you have installed
 SALOME (the directory where this file is located).
 
 """
+        if src.architecture.is_windows():
+            readme_header = readme_header.replace('$$ROOT','%ROOT%')
         readme_compilation_with_binaries="""
 
 compilation based on the binaries used as prerequisites
@@ -1316,6 +1436,16 @@ The procedure to do it is:
 
         if options.binaries or options.sources:
             d['application'] = config.VARS.application
+            d['BINARIES']    = config.INTERNAL.config.binary_dir
+            d['SEPARATOR'] = config.VARS.sep
+            if src.architecture.is_windows():
+                d['operatingSystem'] = 'Windows'
+                d['PYTHON3'] = 'python3'
+                d['ROOT']    = '%ROOT%'
+            else:
+                d['operatingSystem'] = 'Linux'
+                d['PYTHON3'] = ''
+                d['ROOT']    = '$ROOT'
             f.write("# Application: " + d['application'] + "\n")
             if 'KERNEL' in config.APPLICATION.products:
                 VersionSalome = src.get_salome_version(config)
@@ -1336,7 +1466,7 @@ The procedure to do it is:
         if options.sources:
             f.write(src.template.substitute(readme_template_path_src, d))
 
-        if options.binaries and options.sources:
+        if options.binaries and options.sources and not src.architecture.is_windows():
             f.write(readme_compilation_with_binaries)
 
         if options.project:
@@ -1347,7 +1477,7 @@ The procedure to do it is:
     
     return readme_path
 
-def update_config(config, prop, value):
+def update_config(config, logger,  prop, value):
     '''Remove from config.APPLICATION.products the products that have the property given as input.
     
     :param config Config: The global config.
@@ -1363,6 +1493,7 @@ def update_config(config, prop, value):
                 l_product_to_remove.append(product_name)
         for product_name in l_product_to_remove:
             config.APPLICATION.products.__delitem__(product_name)
+            logger.write("Remove product %s with property %s\n" % (product_name, prop), 5)
 
 def description():
     '''method that is called when salomeTools is called with --help option.
@@ -1453,15 +1584,12 @@ Please add it in file:
     
     # Remove the products that are filtered by the --without_properties option
     if options.without_properties:
-        app = runner.cfg.APPLICATION
-        logger.trace("without_properties all products:\n %s\n" % PP.pformat(sorted(app.products.keys())))
         prop, value = options.without_properties
-        update_config(runner.cfg, prop, value)
-        logger.warning("without_properties selected products:\n %s\n" % PP.pformat(sorted(app.products.keys())))
+        update_config(runner.cfg, logger, prop, value)
 
     # Remove from config the products that have the not_in_package property
-    update_config(runner.cfg, "not_in_package", "yes")
-    
+    update_config(runner.cfg, logger, "not_in_package", "yes")
+
     # get the name of the archive or build it
     if options.name:
         if os.path.basename(options.name) == options.name:
@@ -1498,8 +1626,7 @@ Please add it in file:
         if options.project:
             if options.sat:
                 archive_name += "_" 
-            project_name = options.project
-            archive_name += ("satproject_" + project_name)
+            archive_name += ("satproject_" + options.project)
  
         if len(archive_name)==0: # no option worked 
             msg = _("Error: Cannot name the archive\n"
@@ -1510,7 +1637,7 @@ Please add it in file:
             logger.write("\n", 1)
             return 1
  
-    path_targz = os.path.join(dir_name, archive_name + ".tgz")
+    path_targz = os.path.join(dir_name, archive_name + PACKAGE_EXT)
     
     src.printcolors.print_value(logger, "Package path", path_targz, 2)
 
@@ -1542,8 +1669,9 @@ Please add it in file:
         for key in d_bin_files_to_add:
             if key.endswith("(bin)"):
                 source_dir = d_bin_files_to_add[key][0]
-                path_in_archive = d_bin_files_to_add[key][1].replace("BINARIES-" +\
-                   runner.cfg.VARS.dist,runner.cfg.INTERNAL.config.install_dir)
+                path_in_archive = d_bin_files_to_add[key][1].replace(
+                   runner.cfg.INTERNAL.config.binary_dir + runner.cfg.VARS.dist,
+                   runner.cfg.INTERNAL.config.install_dir)
                 if os.path.basename(source_dir)==os.path.basename(path_in_archive):
                     # if basename is the same we will just substitute the dirname 
                     d_paths_to_substitute[os.path.dirname(source_dir)]=\
@@ -1552,7 +1680,6 @@ Please add it in file:
                     d_paths_to_substitute[source_dir]=path_in_archive
 
         d_files_to_add.update(d_bin_files_to_add)
-
     if options.sources:
         d_files_to_add.update(source_package(runner,
                                         runner.cfg,
@@ -1578,8 +1705,8 @@ Please add it in file:
                                   options, logger))
         
     if options.project:
-        DBG.write("config for package %s" % project_name, runner.cfg)
-        d_files_to_add.update(project_package(runner.cfg, project_name, options.project_file_path, options.ftp, tmp_working_dir, options.sat, logger))
+        DBG.write("config for package %s" % options.project, runner.cfg)
+        d_files_to_add.update(project_package(runner.cfg, options.project, options.project_file_path, options.ftp, tmp_working_dir, options.sat, logger))
 
     if not(d_files_to_add):
         msg = _("Error: Empty dictionnary to build the archive!\n")
@@ -1611,7 +1738,10 @@ Please add it in file:
         tar = tarfile.open(path_targz, mode='w:gz')
         
         # get the filtering function if needed
-        filter_function = exclude_VCS_and_extensions
+        if old_python:
+            filter_function = exclude_VCS_and_extensions_26
+        else:
+            filter_function = exclude_VCS_and_extensions
 
         # Add the files to the tarfile object
         res = add_files(tar, archive_name, d_files_to_add, logger, f_exclude=filter_function)