Salome HOME
correction bug spns #16713
[tools/sat.git] / commands / package.py
index 6d796ffe44cb4b3185119d1344fa898317198c50..2d67ac917a56141b0aa0d40911acabbff8703c22 100644 (file)
@@ -23,10 +23,12 @@ import datetime
 import tarfile
 import codecs
 import string
+import pprint as PP
 
 import src
 
 from application import get_SALOME_modules
+import src.debug as DBG
 
 BINARY = "binary"
 SOURCE = "Source"
@@ -90,7 +92,12 @@ parser.add_option('s', 'sources', 'boolean', 'sources',
     _('Optional: Produce a compilable archive of the sources of the '
       'application.'), False)
 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
-    _('Optional: Only source package: do not make archive of vcs products.'),
+    _('Optional: Do not make archive for products in VCS mode (git, cvs, svn). ' 
+      'Sat prepare will use VCS mode instead to retrieve them'),
+    False)
+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('p', 'project', 'string', 'project',
     _('Optional: Produce an archive that contains a project.'), "")
@@ -102,9 +109,9 @@ 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_property', 'string', 'without_property',
+parser.add_option('', 'without_properties', 'properties', 'without_properties',
     _('Optional: Filter the products by their properties.\n\tSyntax: '
-      '--without_property <property>:<value>'))
+      '--without_properties <property>:<value>'))
 
 
 def add_files(tar, name_archive, d_content, logger, f_exclude=None):
@@ -127,14 +134,17 @@ def add_files(tar, name_archive, d_content, logger, f_exclude=None):
     
     success = 0
     # loop over each directory or file stored in the d_content dictionary
-    for name in sorted(d_content.keys()):
+    names = sorted(d_content.keys())
+    DBG.write("add tar names", names)
+
+    for name in names:
         # display information
-        len_points = max_len - len(name)
-        logger.write(name + " " + len_points * "." + " ", 3)
-        # Get the local path and the path in archive 
-        # of the directory or file to add
+        len_points = max_len - len(name) + 3
         local_path, archive_path = d_content[name]
         in_archive = os.path.join(name_archive, archive_path)
+        logger.write(name + " " + len_points * "." + " "+ in_archive + " ", 3)
+        # Get the local path and the path in archive 
+        # of the directory or file to add
         # Add it in the archive
         try:
             tar.add(local_path, arcname=in_archive, exclude=f_exclude)
@@ -191,18 +201,41 @@ def produce_relative_launcher(config,
     else:
         bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin","salome") 
 
+    # check if the application contains an application module
+    # check also if the application has a distene product, 
+    # in this case get its licence file name
+    l_product_info = src.product.get_products_infos(config.APPLICATION.products.keys(), config)
+    salome_application_name="Not defined" 
+    distene_licence_file_name=False
+    for prod_name, prod_info in l_product_info:
+        # look for a "salome application" and a distene product
+        if src.get_property_in_product_cfg(prod_info, "is_distene") == "yes":
+            distene_licence_file_name = src.product.product_has_licence(prod_info, 
+                                            config.PATHS.LICENCEPATH) 
+        if src.get_property_in_product_cfg(prod_info, "is_salome_application") == "yes":
+            salome_application_name=prod_info.name
+
+    # if the application contains an application module, we set ABSOLUTE_APPLI_PATH to it
+    # if not we set it to KERNEL_INSTALL_DIR, which is sufficient, except for salome test
+    if salome_application_name == "Not defined":
+        app_root_dir=kernel_root_dir
+    else:
+        app_root_dir=os.path.join(binaries_dir_name, salome_application_name)
+
     # Get the launcher template and do substitutions
-    withProfile = src.fileEnviron.withProfile
+    if "python3" in config.APPLICATION and config.APPLICATION.python3 == "yes":
+        withProfile = src.fileEnviron.withProfile3
+    else:
+        withProfile = src.fileEnviron.withProfile
 
     withProfile = withProfile.replace(
         "ABSOLUTE_APPLI_PATH'] = 'KERNEL_INSTALL_DIR'",
-        "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + kernel_root_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 + "'")
 
-    before, after = withProfile.split(
-                                "# here your local standalone environment\n")
+    before, after = withProfile.split("# here your local standalone environment\n")
 
     # create an environment file writer
     writer = src.environment.FileEnvWriter(config,
@@ -226,7 +259,9 @@ def produce_relative_launcher(config,
     
     # A hack to put a call to a file for distene licence.
     # It does nothing to an application that has no distene product
-    hack_for_distene_licence(filepath)
+    if distene_licence_file_name:
+        logger.write("Application has a distene licence file! We use it in package launcher", 5)
+        hack_for_distene_licence(filepath, distene_licence_file_name)
        
     # change the rights in order to make the file executable for everybody
     os.chmod(filepath,
@@ -240,7 +275,7 @@ def produce_relative_launcher(config,
 
     return filepath
 
-def hack_for_distene_licence(filepath):
+def hack_for_distene_licence(filepath, licence_file):
     '''Replace the distene licence env variable by a call to a file.
     
     :param filepath Str: The path to the launcher to modify.
@@ -268,10 +303,10 @@ def hack_for_distene_licence(filepath):
     del text[num_line +1]
     text_to_insert ="""    import imp
     try:
-        distene = imp.load_source('distene_licence', '/data/tmpsalome/salome/prerequis/install/LICENSE/dlim8.var.py')
+        distene = imp.load_source('distene_licence', '%s')
         distene.set_distene_variables(context)
     except:
-        pass\n"""
+        pass\n"""  % licence_file
     text.insert(num_line + 1, text_to_insert)
     for line in text:
         fout.write(line)
@@ -464,7 +499,17 @@ def binary_package(config, logger, options, tmp_working_dir):
     l_source_dir = []
     l_not_installed = []
     l_sources_not_present = []
+    generate_mesa_launcher = False  # a flag to know if we generate a mesa launcher
+    if ("APPLICATION" in config  and
+        "properties"  in config.APPLICATION  and
+        "mesa_launcher_in_package"    in config.APPLICATION.properties  and
+        config.APPLICATION.properties.mesa_launcher_in_package == "yes") :
+            generate_mesa_launcher=True
+
     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":
+            continue  
 
         # Add the sources of the products that have the property 
         # sources_in_package : "yes"
@@ -503,10 +548,10 @@ def binary_package(config, logger, options, tmp_working_dir):
          logger.write("""
 WARNING: existing binaries directory from previous detar installation:
          %s
-         To make new package from this, you could
+         To make new package from this, you have to
          1) install binaries in INSTALL directory with the script "install_bin.sh" 
             see README file for more details
-         2) recompile everything in INSTALL with "sat compile" command 
+         2) or recompile everything in INSTALL with "sat compile" command 
             this step is long, and requires some linux packages to be installed 
             on your system\n
 """ % binaries_from_detar)
@@ -573,8 +618,36 @@ WARNING: existing binaries directory from previous detar installation:
                                                  launcher_name,
                                                  binaries_dir_name,
                                                  not(options.without_commercial))
-        
             d_products["launcher"] = (launcher_package, launcher_name)
+
+            # if the application contains mesa products, we generate in addition to the 
+            # classical salome launcher a launcher using mesa and called mesa_salome 
+            # (the mesa launcher will be used for remote usage through ssh).
+            if generate_mesa_launcher:
+                #if there is one : store the use_mesa property
+                restore_use_mesa_option=None
+                if ('properties' in config.APPLICATION and 
+                    'use_mesa' in config.APPLICATION.properties):
+                    restore_use_mesa_option = config.APPLICATION.properties.use_mesa
+
+                # activate mesa property, and generate a mesa launcher
+                src.activate_mesa_property(config)  #activate use_mesa property
+                launcher_mesa_name="mesa_"+launcher_name
+                launcher_package_mesa = produce_relative_launcher(config,
+                                                     logger,
+                                                     tmp_working_dir,
+                                                     launcher_mesa_name,
+                                                     binaries_dir_name,
+                                                     not(options.without_commercial))
+                d_products["launcher (mesa)"] = (launcher_package_mesa, launcher_mesa_name)
+
+                # if there was a use_mesa value, we restore it
+                # else we set it to the default value "no"
+                if restore_use_mesa_option != None:
+                    config.APPLICATION.properties.use_mesa=restore_use_mesa_option
+                else:
+                    config.APPLICATION.properties.use_mesa="no"
+
             if options.sources:
                 # if we mix binaries and sources, we add a copy of the launcher, 
                 # prefixed  with "bin",in order to avoid clashes
@@ -614,10 +687,15 @@ def source_package(sat, config, logger, options, tmp_working_dir):
     :rtype: dict
     '''
     
+    d_archives={}
     # Get all the products that are prepared using an archive
-    logger.write("Find archive products ... ")
-    d_archives, l_pinfo_vcs = get_archives(config, logger)
-    logger.write("Done\n")
+    # unless ftp mode is specified (in this case the user of the
+    # archive will get the sources through the ftp mode of sat prepare
+    if not options.ftp:
+        logger.write("Find archive products ... ")
+        d_archives, l_pinfo_vcs = get_archives(config, logger)
+        logger.write("Done\n")
+
     d_archives_vcs = {}
     if not options.with_vcs and len(l_pinfo_vcs) > 0:
         # Make archives with the products that are not prepared using an archive
@@ -633,8 +711,9 @@ def source_package(sat, config, logger, options, tmp_working_dir):
     # Create a project
     logger.write("Create the project ... ")
     d_project = create_project_for_src_package(config,
-                                                tmp_working_dir,
-                                                options.with_vcs)
+                                               tmp_working_dir,
+                                               options.with_vcs,
+                                               options.ftp)
     logger.write("Done\n")
     
     # Add salomeTools
@@ -679,6 +758,9 @@ def get_archives(config, logger):
     d_archives = {}
     l_pinfo_vcs = []
     for p_name, p_info in l_product_info:
+        # skip product with property not_in_package set to yes
+        if src.get_property_in_product_cfg(p_info, "not_in_package") == "yes":
+            continue  
         # ignore the native and fixed products
         if (src.product.product_is_native(p_info) 
                 or src.product.product_is_fixed(p_info)):
@@ -752,25 +834,38 @@ def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
     # clean the source directory of all the vcs products, then use the source 
     # command and thus construct an archive that will not contain the patches
     l_prod_names = [pn for pn, __ in l_pinfo_vcs]
-    # clean
-    logger.write(_("clean sources\n"))
-    args_clean = config.VARS.application
-    args_clean += " --sources --products "
-    args_clean += ",".join(l_prod_names)
-    sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
-    # source
-    logger.write(_("get sources"))
-    args_source = config.VARS.application
-    args_source += " --products "
-    args_source += ",".join(l_prod_names)
-    sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
-
-    # make the new archives
-    d_archives_vcs = {}
-    for pn, pinfo in l_pinfo_vcs:
-        path_archive = make_archive(pn, pinfo, tmp_working_dir)
-        d_archives_vcs[pn] = (path_archive,
-                              os.path.join(ARCHIVE_DIR, pn + ".tgz"))
+    if False: # clean is dangerous in user/SOURCES, fixed in tmp_local_working_dir
+      logger.write(_("\nclean sources\n"))
+      args_clean = config.VARS.application
+      args_clean += " --sources --products "
+      args_clean += ",".join(l_prod_names)
+      logger.write("WARNING: get_archives_vcs clean\n         '%s'\n" % args_clean, 1)
+      sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
+    if True:
+      # source
+      logger.write(_("get sources\n"))
+      args_source = config.VARS.application
+      args_source += " --products "
+      args_source += ",".join(l_prod_names)
+      svgDir = sat.cfg.APPLICATION.workdir
+      tmp_local_working_dir = os.path.join(sat.cfg.APPLICATION.workdir, "tmp_package")  # to avoid too much big files in /tmp
+      sat.cfg.APPLICATION.workdir = tmp_local_working_dir
+      # DBG.write("SSS sat config.APPLICATION.workdir", sat.cfg.APPLICATION, True)
+      # DBG.write("sat config id", id(sat.cfg), True)
+      # shit as config is not same id() as for sat.source()
+      # sat.source(args_source, batch=True, verbose=5, logger_add_link = logger)
+      import source
+      source.run(args_source, sat, logger) #use this mode as runner.cfg reference
+      
+      # make the new archives
+      d_archives_vcs = {}
+      for pn, pinfo in l_pinfo_vcs:
+          path_archive = make_archive(pn, pinfo, tmp_local_working_dir)
+          logger.write("make archive vcs '%s'\n" % path_archive)
+          d_archives_vcs[pn] = (path_archive,
+                                os.path.join(ARCHIVE_DIR, pn + ".tgz"))
+      sat.cfg.APPLICATION.workdir = svgDir
+      # DBG.write("END sat config", sat.cfg.APPLICATION, True)
     return d_archives_vcs
 
 def make_archive(prod_name, prod_info, where):
@@ -793,7 +888,7 @@ def make_archive(prod_name, prod_info, where):
     tar_prod.close()
     return path_targz_prod       
 
-def create_project_for_src_package(config, tmp_working_dir, with_vcs):
+def create_project_for_src_package(config, tmp_working_dir, with_vcs, with_ftp):
     '''Create a specific project for a source package.
 
     :param config Config: The global configuration.
@@ -802,6 +897,7 @@ def create_project_for_src_package(config, tmp_working_dir, with_vcs):
                                 source package
     :param with_vcs boolean: True if the package is with vcs products (not 
                              transformed into archive products)
+    :param with_ftp boolean: True if the package use ftp servers to get archives
     :return: The dictionary 
              {"project" : (produced project, project path in the archive)}
     :rtype: Dict
@@ -834,6 +930,23 @@ def create_project_for_src_package(config, tmp_working_dir, with_vcs):
     project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
     ff = open(project_pyconf_file, "w")
     ff.write(PROJECT_TEMPLATE)
+    if with_ftp and len(config.PATHS.ARCHIVEFTP) > 0:
+        ftp_path='ARCHIVEFTP : "'+config.PATHS.ARCHIVEFTP[0]
+        for ftpserver in config.PATHS.ARCHIVEFTP[1:]:
+            ftp_path=ftp_path+":"+ftpserver
+        ftp_path+='"'
+        ff.write("# ftp servers where to search for prerequisite archives\n")
+        ff.write(ftp_path)
+    # add licence paths if any
+    if len(config.PATHS.LICENCEPATH) > 0:  
+        licence_path='LICENCEPATH : "'+config.PATHS.LICENCEPATH[0]
+        for path in config.PATHS.LICENCEPATH[1:]:
+            licence_path=licence_path+":"+path
+        licence_path+='"'
+        ff.write("\n# Where to search for licences\n")
+        ff.write(licence_path)
+        
+
     ff.close()
     
     # Loop over the products to get there pyconf and all the scripts 
@@ -842,6 +955,9 @@ def create_project_for_src_package(config, tmp_working_dir, with_vcs):
     lproducts_name = config.APPLICATION.products.keys()
     l_products = src.product.get_products_infos(lproducts_name, config)
     for p_name, p_info in l_products:
+        # skip product with property not_in_package set to yes
+        if src.get_property_in_product_cfg(p_info, "not_in_package") == "yes":
+            continue  
         find_product_scripts_and_pyconf(p_name,
                                         p_info,
                                         config,
@@ -962,22 +1078,71 @@ def find_application_pyconf(config, application_tmp_dir):
     # 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()
 
-def project_package(project_file_path, tmp_working_dir):
+def sat_package(config, tmp_working_dir, options, logger):
+    '''Prepare a dictionary that stores all the needed directories and files to
+       add in a salomeTool package.
+    
+    :param tmp_working_dir str: The temporary local working directory 
+    :param options OptResult: the options of the launched command
+    :return: the dictionary that stores all the needed directories and files to
+             add in a salomeTool package.
+             {label : (path_on_local_machine, path_in_archive)}
+    :rtype: dict
+    '''
+    d_project = {}
+
+    # we include sat himself
+    d_project["all_sat"]=(config.VARS.salometoolsway, "")
+
+    # and we overwrite local.pyconf with a clean wersion.
+    local_pyconf_tmp_path = os.path.join(tmp_working_dir, "local.pyconf")
+    local_file_path = os.path.join(config.VARS.datadir, "local.pyconf")
+    local_cfg = src.pyconf.Config(local_file_path)
+    local_cfg.PROJECTS.project_file_paths=src.pyconf.Sequence(local_cfg.PROJECTS)
+    local_cfg.LOCAL["base"] = "default"
+    local_cfg.LOCAL["workdir"] = "default"
+    local_cfg.LOCAL["log_dir"] = "default"
+    local_cfg.LOCAL["archive_dir"] = "default"
+    local_cfg.LOCAL["VCS"] = "None"
+    local_cfg.LOCAL["tag"] = src.get_salometool_version(config)
+
+    # if the archive contains a project, we write its relative path in local.pyconf
+    if options.project:
+        project_arch_path = os.path.join("projects", options.project, 
+                                         os.path.basename(options.project_file_path))
+        local_cfg.PROJECTS.project_file_paths.append(project_arch_path, "")
+
+    ff = open(local_pyconf_tmp_path, 'w')
+    local_cfg.__save__(ff, 1)
+    ff.close()
+    d_project["local.pyconf"]=(local_pyconf_tmp_path, "data/local.pyconf")
+    return d_project
+    
+
+def project_package(config, name_project, project_file_path, ftp_mode, tmp_working_dir, embedded_in_sat, logger):
     '''Prepare a dictionary that stores all the needed directories and files to
        add in a project package.
     
     :param project_file_path str: The path to the local project.
+    :param ftp_mode boolean: Do not embed archives, the archive will rely on ftp mode to retrieve them.
     :param tmp_working_dir str: The temporary local directory containing some 
                                 specific directories or files needed in the 
                                 project package
+    :param embedded_in_sat boolean : the project package is embedded in a sat package
     :return: the dictionary that stores all the needed directories and files to
              add in a project package.
              {label : (path_on_local_machine, path_in_archive)}
@@ -985,18 +1150,37 @@ def project_package(project_file_path, tmp_working_dir):
     '''
     d_project = {}
     # Read the project file and get the directories to add to the package
-    project_pyconf_cfg = src.pyconf.Config(project_file_path)
-    paths = {"ARCHIVEPATH" : "archives",
-             "APPLICATIONPATH" : "applications",
+    
+    try: 
+      project_pyconf_cfg = config.PROJECTS.projects.__getattr__(name_project)
+    except:
+      logger.write("""
+WARNING: inexisting config.PROJECTS.projects.%s, try to read now from:\n%s\n""" % (name_project, project_file_path))
+      project_pyconf_cfg = src.pyconf.Config(project_file_path)
+      project_pyconf_cfg.PWD = os.path.dirname(project_file_path)
+    
+    paths = {"APPLICATIONPATH" : "applications",
              "PRODUCTPATH" : "products",
              "JOBPATH" : "jobs",
              "MACHINEPATH" : "machines"}
+    if not ftp_mode:
+        paths["ARCHIVEPATH"] = "archives"
+
     # Loop over the project paths and add it
+    project_file_name = os.path.basename(project_file_path)
     for path in paths:
         if path not in project_pyconf_cfg:
             continue
+        if embedded_in_sat:
+            dest_path = os.path.join("projects", name_project, paths[path])
+            project_file_dest = os.path.join("projects", name_project, project_file_name)
+        else:
+            dest_path = paths[path]
+            project_file_dest = project_file_name
+
         # Add the directory to the files to add in the package
-        d_project[path] = (project_pyconf_cfg[path], paths[path])
+        d_project[path] = (project_pyconf_cfg[path], dest_path)
+
         # Modify the value of the path in the package
         project_pyconf_cfg[path] = src.pyconf.Reference(
                                     project_pyconf_cfg,
@@ -1011,15 +1195,19 @@ def project_package(project_file_path, tmp_working_dir):
     project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
                                                            src.pyconf.DOLLAR,
                                                            'PWD')
+    # we don't want to export these two fields
+    project_pyconf_cfg.__delitem__("file_path")
+    project_pyconf_cfg.__delitem__("PWD")
+    if ftp_mode:
+        project_pyconf_cfg.__delitem__("ARCHIVEPATH")
     
     # Write the project pyconf file
-    project_file_name = os.path.basename(project_file_path)
     project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
     ff = open(project_pyconf_tmp_path, 'w')
     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
     project_pyconf_cfg.__save__(ff, 1)
     ff.close()
-    d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
+    d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_dest)
     
     return d_project
 
@@ -1076,7 +1264,7 @@ The procedure to do it is:
         d = dict()
         d['user'] = config.VARS.user
         d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
-        d['version'] = config.INTERNAL.sat_version
+        d['version'] = src.get_salometool_version(config)
         d['dist'] = config.VARS.dist
         f.write(readme_header_tpl.substitute(d)) # write the general header (common)
 
@@ -1120,14 +1308,15 @@ def update_config(config, prop, value):
     :param prop str: The property to filter
     :param value str: The value of the property to filter
     '''
-    src.check_config_has_application(config)
-    l_product_to_remove = []
-    for product_name in config.APPLICATION.products.keys():
-        prod_cfg = src.product.get_product_config(config, product_name)
-        if src.get_property_in_product_cfg(prod_cfg, prop) == value:
-            l_product_to_remove.append(product_name)
-    for product_name in l_product_to_remove:
-        config.APPLICATION.products.__delitem__(product_name)
+    # if there is no APPLICATION (ex sat package -t) : nothing to do
+    if "APPLICATION" in config:
+        l_product_to_remove = []
+        for product_name in config.APPLICATION.products.keys():
+            prod_cfg = src.product.get_product_config(config, product_name)
+            if src.get_property_in_product_cfg(prod_cfg, prop) == value:
+                l_product_to_remove.append(product_name)
+        for product_name in l_product_to_remove:
+            config.APPLICATION.products.__delitem__(product_name)
 
 def description():
     '''method that is called when salomeTools is called with --help option.
@@ -1135,14 +1324,21 @@ def description():
     :return: The text to display for the package command description.
     :rtype: str
     '''
-    return _("The package command creates an archive.\nThere are 4 kinds of "
-             "archive, which can be mixed:\n  1- The binary archive. It contains all the product "
-             "installation directories and a launcher,\n  2- The sources archive."
-             " It contains the products archives, a project corresponding to "
-             "the application and salomeTools,\n  3- The project archive. It "
-             "contains a project (give the project file path as argument),\n  4-"
-             " The salomeTools archive. It contains salomeTools.\n\nexample:"
-             "\nsat package SALOME-master --bineries --sources")
+    return _("""
+The package command creates a tar file archive of a product.
+There are four kinds of archive, which can be mixed:
+
+ 1 - The binary archive. 
+     It contains the product installation directories plus a launcher.
+ 2 - The sources archive. 
+     It contains the product archives, a project (the application plus salomeTools).
+ 3 - The project archive. 
+     It contains a project (give the project file path as argument).
+ 4 - The salomeTools archive. 
+     It contains code utility salomeTools.
+
+example:
+ >> sat package SALOME-master --binaries --sources""")
   
 def run(args, runner, logger):
     '''method that is called when salomeTools is called with package parameter.
@@ -1179,8 +1375,7 @@ def run(args, runner, logger):
                                                     runner.cfg.VARS.application), 1)
         
         # Get the default directory where to put the packages
-        package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
-                                            "PACKAGE")
+        package_default_path = os.path.join(runner.cfg.APPLICATION.workdir, "PACKAGE")
         src.ensure_path_exists(package_default_path)
         
     # if the package contains a project:
@@ -1195,9 +1390,7 @@ def run(args, runner, logger):
                 break
 
         if foundProject is None:
-            local_path = os.path.join(runner.cfg.VARS.salometoolsway,
-                                     "data",
-                                     "local.pyconf")
+            local_path = os.path.join(runner.cfg.VARS.salometoolsway, "data", "local.pyconf")
             msg = _("""ERROR: the project %(1)s is not visible by salomeTools.
 known projects are:
 %(2)s
@@ -1212,10 +1405,16 @@ Please add it in file:
             options.project_file_path = foundProject
             src.printcolors.print_value(logger, "Project path", options.project_file_path, 2)
     
-    # Remove the products that are filtered by the --without_property option
-    if options.without_property:
-        [prop, value] = options.without_property.split(":")
+    # 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())))
+
+    # Remove from config the products that have the not_in_package property
+    update_config(runner.cfg, "not_in_package", "yes")
     
     # get the name of the archive or build it
     if options.name:
@@ -1247,12 +1446,15 @@ Please add it in file:
             if options.with_vcs:
                 archive_name += "-VCS"
 
+        if options.sat:
+            archive_name += ("salomeTools_" + src.get_salometool_version(runner.cfg))
+
         if options.project:
+            if options.sat:
+                archive_name += "_" 
             project_name = options.project
-            archive_name += ("PROJECT-" + project_name)
+            archive_name += ("satproject_" + project_name)
  
-        if options.sat:
-            archive_name += ("salomeTools_" + runner.cfg.INTERNAL.sat_version)
         if len(archive_name)==0: # no option worked 
             msg = _("Error: Cannot name the archive\n"
                     " check if at least one of the following options was "
@@ -1268,8 +1470,7 @@ Please add it in file:
 
     # Create a working directory for all files that are produced during the
     # package creation and that will be removed at the end of the command
-    tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
-                                   runner.cfg.VARS.datehour)
+    tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root, runner.cfg.VARS.datehour)
     src.ensure_path_exists(tmp_working_dir)
     logger.write("\n", 5)
     logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
@@ -1279,7 +1480,7 @@ Please add it in file:
     msg = _("Preparation of files to add to the archive")
     logger.write(src.printcolors.printcLabel(msg), 2)
     logger.write("\n", 2)
-
+    
     d_files_to_add={}  # content of the archive
 
     # a dict to hold paths that will need to be substitute for users recompilations
@@ -1326,11 +1527,12 @@ Please add it in file:
         # --salomeTool option is not considered when --sources is selected, as this option
         # already brings salomeTool!
         if options.sat:
-            d_files_to_add.update({"salomeTools" : (runner.cfg.VARS.salometoolsway, "")})
+            d_files_to_add.update(sat_package(runner.cfg, tmp_working_dir, 
+                                  options, logger))
         
-    
     if options.project:
-        d_files_to_add.update(project_package(options.project_file_path, tmp_working_dir))
+        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))
 
     if not(d_files_to_add):
         msg = _("Error: Empty dictionnary to build the archive!\n")
@@ -1339,9 +1541,7 @@ Please add it in file:
         return 1
 
     # Add the README file in the package
-    local_readme_tmp_path = add_readme(runner.cfg,
-                                       options,
-                                       tmp_working_dir)
+    local_readme_tmp_path = add_readme(runner.cfg, options, tmp_working_dir)
     d_files_to_add["README"] = (local_readme_tmp_path, "README")
 
     # Add the additional files of option add_files
@@ -1354,10 +1554,11 @@ Please add it in file:
             d_files_to_add[file_name] = (file_path, file_name)
 
     logger.write("\n", 2)
-
     logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
     logger.write("\n", 2)
-    
+    logger.write("\nfiles and directories to add:\n%s\n\n" % PP.pformat(d_files_to_add), 5)
+
+    res = 0
     try:
         # Creating the object tarfile
         tar = tarfile.open(path_targz, mode='w:gz')
@@ -1370,15 +1571,27 @@ Please add it in file:
         tar.close()
     except KeyboardInterrupt:
         logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
-        logger.write(_("Removing the temporary working directory ... "), 1)
+        logger.write(_("Removing the temporary working directory '%s'... ") % tmp_working_dir, 1)
         # remove the working directory
         shutil.rmtree(tmp_working_dir)
         logger.write(_("OK"), 1)
         logger.write(_("\n"), 1)
         return 1
     
-    # remove the working directory    
-    shutil.rmtree(tmp_working_dir)
+    # case if no application, only package sat as 'sat package -t'
+    try:
+        app = runner.cfg.APPLICATION
+    except:
+        app = None
+
+    # unconditionaly remove the tmp_local_working_dir
+    if app is not None:
+        tmp_local_working_dir = os.path.join(app.workdir, "tmp_package")
+        if os.path.isdir(tmp_local_working_dir):
+            shutil.rmtree(tmp_local_working_dir)
+
+    # have to decide some time
+    DBG.tofix("make shutil.rmtree('%s') effective" % tmp_working_dir, "", DBG.isDeveloper())
     
     # Print again the path of the package
     logger.write("\n", 2)