Salome HOME
merge dev_package into BR_8_3
[tools/sat.git] / commands / package.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2012  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 import os
20 import stat
21 import shutil
22 import datetime
23 import tarfile
24 import codecs
25 import string
26
27 import src
28
29 from application import get_SALOME_modules
30
31 BINARY = "binary"
32 SOURCE = "Source"
33 PROJECT = "Project"
34 SAT = "Sat"
35
36 ARCHIVE_DIR = "ARCHIVES"
37 PROJECT_DIR = "PROJECT"
38
39 IGNORED_DIRS = [".git", ".svn"]
40 IGNORED_EXTENSIONS = []
41
42 PROJECT_TEMPLATE = """#!/usr/bin/env python
43 #-*- coding:utf-8 -*-
44
45 # The path to the archive root directory
46 root_path : $PWD + "/../"
47 # path to the PROJECT
48 project_path : $PWD + "/"
49
50 # Where to search the archives of the products
51 ARCHIVEPATH : $root_path + "ARCHIVES"
52 # Where to search the pyconf of the applications
53 APPLICATIONPATH : $project_path + "applications/"
54 # Where to search the pyconf of the products
55 PRODUCTPATH : $project_path + "products/"
56 # Where to search the pyconf of the jobs of the project
57 JOBPATH : $project_path + "jobs/"
58 # Where to search the pyconf of the machines of the project
59 MACHINEPATH : $project_path + "machines/"
60 """
61
62 SITE_TEMPLATE = ("""#!/usr/bin/env python
63 #-*- coding:utf-8 -*-
64
65 SITE :
66 {   
67     log :
68     {
69         log_dir : $USER.workdir + "/LOGS"
70     }
71     test :{
72            tmp_dir_with_application : '/tmp' + $VARS.sep + $VARS.user + """
73 """$VARS.sep + $APPLICATION.name + $VARS.sep + 'test'
74            tmp_dir : '/tmp' + $VARS.sep + $VARS.user + $VARS.sep + 'test'
75            timeout : 150
76            }
77 }
78
79 PROJECTS :
80 {
81 project_file_paths : [$VARS.salometoolsway + $VARS.sep + \"..\" + $VARS.sep"""
82 """ + \"""" + PROJECT_DIR + """\" + $VARS.sep + "project.pyconf"]
83 }
84 """)
85
86 # Define all possible option for the package command :  sat package <options>
87 parser = src.options.Options()
88 parser.add_option('b', 'binaries', 'boolean', 'binaries',
89     _('Optional: Produce a binary package.'), False)
90 parser.add_option('f', 'force_creation', 'boolean', 'force_creation',
91     _('Optional: Only binary package: produce the archive even if '
92       'there are some missing products.'), False)
93 parser.add_option('s', 'sources', 'boolean', 'sources',
94     _('Optional: Produce a compilable archive of the sources of the '
95       'application.'), False)
96 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
97     _('Optional: Only source package: do not make archive of vcs products.'),
98     False)
99 parser.add_option('p', 'project', 'string', 'project',
100     _('Optional: Produce an archive that contains a project.'), "")
101 parser.add_option('t', 'salometools', 'boolean', 'sat',
102     _('Optional: Produce an archive that contains salomeTools.'), False)
103 parser.add_option('n', 'name', 'string', 'name',
104     _('Optional: The name or full path of the archive.'), None)
105 parser.add_option('', 'add_files', 'list2', 'add_files',
106     _('Optional: The list of additional files to add to the archive.'), [])
107 parser.add_option('', 'without_commercial', 'boolean', 'without_commercial',
108     _('Optional: do not add commercial licence.'), False)
109 parser.add_option('', 'without_property', 'string', 'without_property',
110     _('Optional: Filter the products by their properties.\n\tSyntax: '
111       '--without_property <property>:<value>'))
112
113
114 def add_files(tar, name_archive, d_content, logger, f_exclude=None):
115     '''Create an archive containing all directories and files that are given in
116        the d_content argument.
117     
118     :param tar tarfile: The tarfile instance used to make the archive.
119     :param name_archive str: The name of the archive to make.
120     :param d_content dict: The dictionary that contain all directories and files
121                            to add in the archive.
122                            d_content[label] = 
123                                         (path_on_local_machine, path_in_archive)
124     :param logger Logger: the logging instance
125     :param f_exclude Function: the function that filters
126     :return: 0 if success, 1 if not.
127     :rtype: int
128     '''
129     # get the max length of the messages in order to make the display
130     max_len = len(max(d_content.keys(), key=len))
131     
132     success = 0
133     # loop over each directory or file stored in the d_content dictionary
134     for name in d_content.keys():
135         # display information
136         len_points = max_len - len(name)
137         logger.write(name + " " + len_points * "." + " ", 3)
138         # Get the local path and the path in archive 
139         # of the directory or file to add
140         local_path, archive_path = d_content[name]
141         in_archive = os.path.join(name_archive, archive_path)
142         # Add it in the archive
143         try:
144             tar.add(local_path, arcname=in_archive, exclude=f_exclude)
145             logger.write(src.printcolors.printcSuccess(_("OK")), 3)
146         except Exception as e:
147             logger.write(src.printcolors.printcError(_("KO ")), 3)
148             logger.write(str(e), 3)
149             success = 1
150         logger.write("\n", 3)
151     return success
152
153 def exclude_VCS_and_extensions(filename):
154     ''' The function that is used to exclude from package the link to the 
155         VCS repositories (like .git)
156
157     :param filename Str: The filname to exclude (or not).
158     :return: True if the file has to be exclude
159     :rtype: Boolean
160     '''
161     for dir_name in IGNORED_DIRS:
162         if dir_name in filename:
163             return True
164     for extension in IGNORED_EXTENSIONS:
165         if filename.endswith(extension):
166             return True
167     return False
168
169 def produce_relative_launcher(config,
170                               logger,
171                               file_dir,
172                               file_name,
173                               binaries_dir_name,
174                               with_commercial=True):
175     '''Create a specific SALOME launcher for the binary package. This launcher 
176        uses relative paths.
177     
178     :param config Config: The global configuration.
179     :param logger Logger: the logging instance
180     :param file_dir str: the directory where to put the launcher
181     :param file_name str: The launcher name
182     :param binaries_dir_name str: the name of the repository where the binaries
183                                   are, in the archive.
184     :return: the path of the produced launcher
185     :rtype: str
186     '''
187     
188     # Get the launcher template
189     profile_install_dir = os.path.join(binaries_dir_name,
190                                        config.APPLICATION.profile.product)
191     withProfile = src.fileEnviron.withProfile
192     withProfile = withProfile.replace(
193         "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
194         "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
195     withProfile = withProfile.replace(
196         "os.path.join( 'PROFILE_INSTALL_DIR'",
197         "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
198
199     before, after = withProfile.split(
200                                 "# here your local standalone environment\n")
201
202     # create an environment file writer
203     writer = src.environment.FileEnvWriter(config,
204                                            logger,
205                                            file_dir,
206                                            src_root=None)
207     
208     filepath = os.path.join(file_dir, file_name)
209     # open the file and write into it
210     launch_file = open(filepath, "w")
211     launch_file.write(before)
212     # Write
213     writer.write_cfgForPy_file(launch_file,
214                                for_package = binaries_dir_name,
215                                with_commercial=with_commercial)
216     launch_file.write(after)
217     launch_file.close()
218     
219     # Little hack to put out_dir_Path outside the strings
220     src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
221     
222     # change the rights in order to make the file executable for everybody
223     os.chmod(filepath,
224              stat.S_IRUSR |
225              stat.S_IRGRP |
226              stat.S_IROTH |
227              stat.S_IWUSR |
228              stat.S_IXUSR |
229              stat.S_IXGRP |
230              stat.S_IXOTH)
231
232     return filepath
233
234 def produce_relative_env_files(config,
235                               logger,
236                               file_dir,
237                               binaries_dir_name):
238     '''Create some specific environment files for the binary package. These 
239        files use relative paths.
240     
241     :param config Config: The global configuration.
242     :param logger Logger: the logging instance
243     :param file_dir str: the directory where to put the files
244     :param binaries_dir_name str: the name of the repository where the binaries
245                                   are, in the archive.
246     :return: the list of path of the produced environment files
247     :rtype: List
248     '''  
249     # create an environment file writer
250     writer = src.environment.FileEnvWriter(config,
251                                            logger,
252                                            file_dir,
253                                            src_root=None)
254     
255     # Write
256     filepath = writer.write_env_file("env_launch.sh",
257                           False, # for launch
258                           "bash",
259                           for_package = binaries_dir_name)
260
261     # Little hack to put out_dir_Path as environment variable
262     src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
263
264     # change the rights in order to make the file executable for everybody
265     os.chmod(filepath,
266              stat.S_IRUSR |
267              stat.S_IRGRP |
268              stat.S_IROTH |
269              stat.S_IWUSR |
270              stat.S_IXUSR |
271              stat.S_IXGRP |
272              stat.S_IXOTH)
273     
274     return filepath
275
276 def produce_install_bin_file(config,
277                              logger,
278                              file_dir,
279                              d_sub,
280                              file_name):
281     '''Create a bash shell script which do substitutions in BIRARIES dir 
282        in order to use it for extra compilations.
283     
284     :param config Config: The global configuration.
285     :param logger Logger: the logging instance
286     :param file_dir str: the directory where to put the files
287     :param d_sub, dict: the dictionnary that contains the substitutions to be done
288     :param file_name str: the name of the install script file
289     :return: the produced file
290     :rtype: str
291     '''  
292     # Write
293     filepath = os.path.join(file_dir, file_name)
294     # open the file and write into it
295     # use codec utf-8 as sat variables are in unicode
296     with codecs.open(filepath, "w", 'utf-8') as installbin_file:
297         installbin_template_path = os.path.join(config.VARS.internal_dir,
298                                         "INSTALL_BIN.template")
299         
300         # build the name of the directory that will contain the binaries
301         binaries_dir_name = "BINARIES-" + config.VARS.dist
302         # build the substitution loop
303         loop_cmd = "for f in $(grep -RIl"
304         for key in d_sub:
305             loop_cmd += " -e "+ key
306         loop_cmd += ' INSTALL); do\n     sed -i "\n'
307         for key in d_sub:
308             loop_cmd += "        s?" + key + "?$(pwd)/" + d_sub[key] + "?g\n"
309         loop_cmd += '            " $f\ndone'
310
311         d={}
312         d["BINARIES_DIR"] = binaries_dir_name
313         d["SUBSTITUTION_LOOP"]=loop_cmd
314         
315         # substitute the template and write it in file
316         content=src.template.substitute(installbin_template_path, d)
317         installbin_file.write(content)
318         # change the rights in order to make the file executable for everybody
319         os.chmod(filepath,
320                  stat.S_IRUSR |
321                  stat.S_IRGRP |
322                  stat.S_IROTH |
323                  stat.S_IWUSR |
324                  stat.S_IXUSR |
325                  stat.S_IXGRP |
326                  stat.S_IXOTH)
327     
328     return filepath
329
330 def product_appli_creation_script(config,
331                                   logger,
332                                   file_dir,
333                                   binaries_dir_name):
334     '''Create a script that can produce an application (EDF style) in the binary
335        package.
336     
337     :param config Config: The global configuration.
338     :param logger Logger: the logging instance
339     :param file_dir str: the directory where to put the file
340     :param binaries_dir_name str: the name of the repository where the binaries
341                                   are, in the archive.
342     :return: the path of the produced script file
343     :rtype: Str
344     '''
345     template_name = "create_appli.py.for_bin_packages.template"
346     template_path = os.path.join(config.VARS.internal_dir, template_name)
347     text_to_fill = open(template_path, "r").read()
348     text_to_fill = text_to_fill.replace("TO BE FILLED 1",
349                                         '"' + binaries_dir_name + '"')
350     
351     text_to_add = ""
352     for product_name in get_SALOME_modules(config):
353         product_info = src.product.get_product_config(config, product_name)
354        
355         if src.product.product_is_smesh_plugin(product_info):
356             continue
357
358         if 'install_dir' in product_info and bool(product_info.install_dir):
359             if src.product.product_is_cpp(product_info):
360                 # cpp module
361                 for cpp_name in src.product.get_product_components(product_info):
362                     line_to_add = ("<module name=\"" + 
363                                    cpp_name + 
364                                    "\" gui=\"yes\" path=\"''' + "
365                                    "os.path.join(dir_bin_name, \"" + 
366                                    cpp_name + "\") + '''\"/>")
367             else:
368                 # regular module
369                 line_to_add = ("<module name=\"" + 
370                                product_name + 
371                                "\" gui=\"yes\" path=\"''' + "
372                                "os.path.join(dir_bin_name, \"" + 
373                                product_name + "\") + '''\"/>")
374             text_to_add += line_to_add + "\n"
375     
376     filled_text = text_to_fill.replace("TO BE FILLED 2", text_to_add)
377     
378     tmp_file_path = os.path.join(file_dir, "create_appli.py")
379     ff = open(tmp_file_path, "w")
380     ff.write(filled_text)
381     ff.close()
382     
383     # change the rights in order to make the file executable for everybody
384     os.chmod(tmp_file_path,
385              stat.S_IRUSR |
386              stat.S_IRGRP |
387              stat.S_IROTH |
388              stat.S_IWUSR |
389              stat.S_IXUSR |
390              stat.S_IXGRP |
391              stat.S_IXOTH)
392     
393     return tmp_file_path
394
395 def binary_package(config, logger, options, tmp_working_dir):
396     '''Prepare a dictionary that stores all the needed directories and files to
397        add in a binary package.
398     
399     :param config Config: The global configuration.
400     :param logger Logger: the logging instance
401     :param options OptResult: the options of the launched command
402     :param tmp_working_dir str: The temporary local directory containing some 
403                                 specific directories or files needed in the 
404                                 binary package
405     :return: the dictionary that stores all the needed directories and files to
406              add in a binary package.
407              {label : (path_on_local_machine, path_in_archive)}
408     :rtype: dict
409     '''
410
411     # Get the list of product installation to add to the archive
412     l_products_name = config.APPLICATION.products.keys()
413     l_product_info = src.product.get_products_infos(l_products_name,
414                                                     config)
415     l_install_dir = []
416     l_source_dir = []
417     l_not_installed = []
418     l_sources_not_present = []
419     for prod_name, prod_info in l_product_info:
420
421         # Add the sources of the products that have the property 
422         # sources_in_package : "yes"
423         if src.get_property_in_product_cfg(prod_info,
424                                            "sources_in_package") == "yes":
425             if os.path.exists(prod_info.source_dir):
426                 l_source_dir.append((prod_name, prod_info.source_dir))
427             else:
428                 l_sources_not_present.append(prod_name)
429
430         # ignore the native and fixed products for install directories
431         if (src.product.product_is_native(prod_info) 
432                 or src.product.product_is_fixed(prod_info)
433                 or not src.product.product_compiles(prod_info)):
434             continue
435         if src.product.check_installation(prod_info):
436             l_install_dir.append((prod_name, prod_info.install_dir))
437         else:
438             l_not_installed.append(prod_name)
439         
440         # Add also the cpp generated modules (if any)
441         if src.product.product_is_cpp(prod_info):
442             # cpp module
443             for name_cpp in src.product.get_product_components(prod_info):
444                 install_dir = os.path.join(config.APPLICATION.workdir,
445                                            "INSTALL", name_cpp) 
446                 if os.path.exists(install_dir):
447                     l_install_dir.append((name_cpp, install_dir))
448                 else:
449                     l_not_installed.append(name_cpp)
450         
451     # Print warning or error if there are some missing products
452     if len(l_not_installed) > 0:
453         text_missing_prods = ""
454         for p_name in l_not_installed:
455             text_missing_prods += "-" + p_name + "\n"
456         if not options.force_creation:
457             msg = _("ERROR: there are missing products installations:")
458             logger.write("%s\n%s" % (src.printcolors.printcError(msg),
459                                      text_missing_prods),
460                          1)
461             return None
462         else:
463             msg = _("WARNING: there are missing products installations:")
464             logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
465                                      text_missing_prods),
466                          1)
467
468     # Do the same for sources
469     if len(l_sources_not_present) > 0:
470         text_missing_prods = ""
471         for p_name in l_sources_not_present:
472             text_missing_prods += "-" + p_name + "\n"
473         if not options.force_creation:
474             msg = _("ERROR: there are missing products sources:")
475             logger.write("%s\n%s" % (src.printcolors.printcError(msg),
476                                      text_missing_prods),
477                          1)
478             return None
479         else:
480             msg = _("WARNING: there are missing products sources:")
481             logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
482                                      text_missing_prods),
483                          1)
484  
485     # construct the name of the directory that will contain the binaries
486     binaries_dir_name = "BINARIES-" + config.VARS.dist
487     
488     # construct the correlation table between the product names, there 
489     # actual install directories and there install directory in archive
490     d_products = {}
491     for prod_name, install_dir in l_install_dir:
492         path_in_archive = os.path.join(binaries_dir_name, prod_name)
493         d_products[prod_name + " (bin)"] = (install_dir, path_in_archive)
494         
495     for prod_name, source_dir in l_source_dir:
496         path_in_archive = os.path.join("SOURCES", prod_name)
497         d_products[prod_name + " (sources)"] = (source_dir, path_in_archive)
498
499     # create the relative launcher and add it to the files to add
500     if ("profile" in config.APPLICATION and 
501                                        "product" in config.APPLICATION.profile):
502         launcher_name = config.APPLICATION.profile.launcher_name
503         launcher_package = produce_relative_launcher(config,
504                                              logger,
505                                              tmp_working_dir,
506                                              launcher_name,
507                                              binaries_dir_name,
508                                              not(options.without_commercial))
509     
510         d_products["launcher"] = (launcher_package, launcher_name)
511         if options.sources:
512             # if we mix binaries and sources, we add a copy of the launcher, 
513             # prefixed  with "bin",in order to avoid clashes
514             d_products["launcher (copy)"] = (launcher_package, "bin"+launcher_name)
515     else:
516         # Provide a script for the creation of an application EDF style
517         appli_script = product_appli_creation_script(config,
518                                                     logger,
519                                                     tmp_working_dir,
520                                                     binaries_dir_name)
521         
522         d_products["appli script"] = (appli_script, "create_appli.py")
523
524     # Put also the environment file
525     env_file = produce_relative_env_files(config,
526                                            logger,
527                                            tmp_working_dir,
528                                            binaries_dir_name)
529
530     d_products["environment file"] = (env_file, "env_launch.sh")
531       
532     return d_products
533
534 def source_package(sat, config, logger, options, tmp_working_dir):
535     '''Prepare a dictionary that stores all the needed directories and files to
536        add in a source package.
537     
538     :param config Config: The global configuration.
539     :param logger Logger: the logging instance
540     :param options OptResult: the options of the launched command
541     :param tmp_working_dir str: The temporary local directory containing some 
542                                 specific directories or files needed in the 
543                                 binary package
544     :return: the dictionary that stores all the needed directories and files to
545              add in a source package.
546              {label : (path_on_local_machine, path_in_archive)}
547     :rtype: dict
548     '''
549     
550     # Get all the products that are prepared using an archive
551     logger.write("Find archive products ... ")
552     d_archives, l_pinfo_vcs = get_archives(config, logger)
553     logger.write("Done\n")
554     d_archives_vcs = {}
555     if not options.with_vcs and len(l_pinfo_vcs) > 0:
556         # Make archives with the products that are not prepared using an archive
557         # (git, cvs, svn, etc)
558         logger.write("Construct archives for vcs products ... ")
559         d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
560                                           sat,
561                                           config,
562                                           logger,
563                                           tmp_working_dir)
564         logger.write("Done\n")
565
566     # Create a project
567     logger.write("Create the project ... ")
568     d_project = create_project_for_src_package(config,
569                                                 tmp_working_dir,
570                                                 options.with_vcs)
571     logger.write("Done\n")
572     
573     # Add salomeTools
574     tmp_sat = add_salomeTools(config, tmp_working_dir)
575     d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
576     
577     # Add a sat symbolic link if not win
578     if not src.architecture.is_windows():
579         tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
580         try:
581             t = os.getcwd()
582         except:
583             # In the jobs, os.getcwd() can fail
584             t = config.USER.workdir
585         os.chdir(tmp_working_dir)
586         if os.path.lexists(tmp_satlink_path):
587             os.remove(tmp_satlink_path)
588         os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
589         os.chdir(t)
590         
591         d_sat["sat link"] = (tmp_satlink_path, "sat")
592     
593     d_source = src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
594     return d_source
595
596 def get_archives(config, logger):
597     '''Find all the products that are get using an archive and all the products
598        that are get using a vcs (git, cvs, svn) repository.
599     
600     :param config Config: The global configuration.
601     :param logger Logger: the logging instance
602     :return: the dictionary {name_product : 
603              (local path of its archive, path in the package of its archive )}
604              and the list of specific configuration corresponding to the vcs 
605              products
606     :rtype: (Dict, List)
607     '''
608     # Get the list of product informations
609     l_products_name = config.APPLICATION.products.keys()
610     l_product_info = src.product.get_products_infos(l_products_name,
611                                                     config)
612     d_archives = {}
613     l_pinfo_vcs = []
614     for p_name, p_info in l_product_info:
615         # ignore the native and fixed products
616         if (src.product.product_is_native(p_info) 
617                 or src.product.product_is_fixed(p_info)):
618             continue
619         if p_info.get_source == "archive":
620             archive_path = p_info.archive_info.archive_name
621             archive_name = os.path.basename(archive_path)
622         else:
623             l_pinfo_vcs.append((p_name, p_info))
624             
625         d_archives[p_name] = (archive_path,
626                               os.path.join(ARCHIVE_DIR, archive_name))
627     return d_archives, l_pinfo_vcs
628
629 def add_salomeTools(config, tmp_working_dir):
630     '''Prepare a version of salomeTools that has a specific site.pyconf file 
631        configured for a source package.
632
633     :param config Config: The global configuration.
634     :param tmp_working_dir str: The temporary local directory containing some 
635                                 specific directories or files needed in the 
636                                 source package
637     :return: The path to the local salomeTools directory to add in the package
638     :rtype: str
639     '''
640     # Copy sat in the temporary working directory
641     sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
642     sat_running_path = src.Path(config.VARS.salometoolsway)
643     sat_running_path.copy(sat_tmp_path)
644     
645     # Update the site.pyconf file that contains the path to the project
646     site_pyconf_name = "site.pyconf"
647     site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
648     site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
649     ff = open(site_pyconf_file, "w")
650     ff.write(SITE_TEMPLATE)
651     ff.close()
652     
653     return sat_tmp_path.path
654
655 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
656     '''For sources package that require that all products are get using an 
657        archive, one has to create some archive for the vcs products.
658        So this method calls the clean and source command of sat and then create
659        the archives.
660
661     :param l_pinfo_vcs List: The list of specific configuration corresponding to
662                              each vcs product
663     :param sat Sat: The Sat instance that can be called to clean and source the
664                     products
665     :param config Config: The global configuration.
666     :param logger Logger: the logging instance
667     :param tmp_working_dir str: The temporary local directory containing some 
668                                 specific directories or files needed in the 
669                                 source package
670     :return: the dictionary that stores all the archives to add in the source 
671              package. {label : (path_on_local_machine, path_in_archive)}
672     :rtype: dict
673     '''
674     # clean the source directory of all the vcs products, then use the source 
675     # command and thus construct an archive that will not contain the patches
676     l_prod_names = [pn for pn, __ in l_pinfo_vcs]
677     # clean
678     logger.write(_("clean sources\n"))
679     args_clean = config.VARS.application
680     args_clean += " --sources --products "
681     args_clean += ",".join(l_prod_names)
682     sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
683     # source
684     logger.write(_("get sources"))
685     args_source = config.VARS.application
686     args_source += " --products "
687     args_source += ",".join(l_prod_names)
688     sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
689
690     # make the new archives
691     d_archives_vcs = {}
692     for pn, pinfo in l_pinfo_vcs:
693         path_archive = make_archive(pn, pinfo, tmp_working_dir)
694         d_archives_vcs[pn] = (path_archive,
695                               os.path.join(ARCHIVE_DIR, pn + ".tgz"))
696     return d_archives_vcs
697
698 def make_archive(prod_name, prod_info, where):
699     '''Create an archive of a product by searching its source directory.
700
701     :param prod_name str: The name of the product.
702     :param prod_info Config: The specific configuration corresponding to the 
703                              product
704     :param where str: The path of the repository where to put the resulting 
705                       archive
706     :return: The path of the resulting archive
707     :rtype: str
708     '''
709     path_targz_prod = os.path.join(where, prod_name + ".tgz")
710     tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
711     local_path = prod_info.source_dir
712     tar_prod.add(local_path, arcname=prod_name)
713     tar_prod.close()
714     return path_targz_prod       
715
716 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
717     '''Create a specific project for a source package.
718
719     :param config Config: The global configuration.
720     :param tmp_working_dir str: The temporary local directory containing some 
721                                 specific directories or files needed in the 
722                                 source package
723     :param with_vcs boolean: True if the package is with vcs products (not 
724                              transformed into archive products)
725     :return: The dictionary 
726              {"project" : (produced project, project path in the archive)}
727     :rtype: Dict
728     '''
729
730     # Create in the working temporary directory the full project tree
731     project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
732     products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
733                                          "products")
734     compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
735                                          "products",
736                                          "compil_scripts")
737     env_scripts_tmp_dir = os.path.join(project_tmp_dir,
738                                          "products",
739                                          "env_scripts")
740     patches_tmp_dir = os.path.join(project_tmp_dir,
741                                          "products",
742                                          "patches")
743     application_tmp_dir = os.path.join(project_tmp_dir,
744                                          "applications")
745     for directory in [project_tmp_dir,
746                       compil_scripts_tmp_dir,
747                       env_scripts_tmp_dir,
748                       patches_tmp_dir,
749                       application_tmp_dir]:
750         src.ensure_path_exists(directory)
751
752     # Create the pyconf that contains the information of the project
753     project_pyconf_name = "project.pyconf"        
754     project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
755     ff = open(project_pyconf_file, "w")
756     ff.write(PROJECT_TEMPLATE)
757     ff.close()
758     
759     # Loop over the products to get there pyconf and all the scripts 
760     # (compilation, environment, patches)
761     # and create the pyconf file to add to the project
762     lproducts_name = config.APPLICATION.products.keys()
763     l_products = src.product.get_products_infos(lproducts_name, config)
764     for p_name, p_info in l_products:
765         # ignore native and fixed products
766         if src.product.product_is_native(p_info):
767             continue
768         find_product_scripts_and_pyconf(p_name,
769                                         p_info,
770                                         config,
771                                         with_vcs,
772                                         compil_scripts_tmp_dir,
773                                         env_scripts_tmp_dir,
774                                         patches_tmp_dir,
775                                         products_pyconf_tmp_dir)
776     
777     find_application_pyconf(config, application_tmp_dir)
778     
779     d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
780     return d_project
781
782 def find_product_scripts_and_pyconf(p_name,
783                                     p_info,
784                                     config,
785                                     with_vcs,
786                                     compil_scripts_tmp_dir,
787                                     env_scripts_tmp_dir,
788                                     patches_tmp_dir,
789                                     products_pyconf_tmp_dir):
790     '''Create a specific pyconf file for a given product. Get its environment 
791        script, its compilation script and patches and put it in the temporary
792        working directory. This method is used in the source package in order to
793        construct the specific project.
794
795     :param p_name str: The name of the product.
796     :param p_info Config: The specific configuration corresponding to the 
797                              product
798     :param config Config: The global configuration.
799     :param with_vcs boolean: True if the package is with vcs products (not 
800                              transformed into archive products)
801     :param compil_scripts_tmp_dir str: The path to the temporary compilation 
802                                        scripts directory of the project.
803     :param env_scripts_tmp_dir str: The path to the temporary environment script 
804                                     directory of the project.
805     :param patches_tmp_dir str: The path to the temporary patch scripts 
806                                 directory of the project.
807     :param products_pyconf_tmp_dir str: The path to the temporary product 
808                                         scripts directory of the project.
809     '''
810     
811     # read the pyconf of the product
812     product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
813                                            config.PATHS.PRODUCTPATH)
814     product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
815
816     # find the compilation script if any
817     if src.product.product_has_script(p_info):
818         compil_script_path = src.Path(p_info.compil_script)
819         compil_script_path.copy(compil_scripts_tmp_dir)
820         product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
821                                                     p_info.compil_script)
822     # find the environment script if any
823     if src.product.product_has_env_script(p_info):
824         env_script_path = src.Path(p_info.environ.env_script)
825         env_script_path.copy(env_scripts_tmp_dir)
826         product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
827                                                 p_info.environ.env_script)
828     # find the patches if any
829     if src.product.product_has_patches(p_info):
830         patches = src.pyconf.Sequence()
831         for patch_path in p_info.patches:
832             p_path = src.Path(patch_path)
833             p_path.copy(patches_tmp_dir)
834             patches.append(os.path.basename(patch_path), "")
835
836         product_pyconf_cfg[p_info.section].patches = patches
837     
838     if with_vcs:
839         # put in the pyconf file the resolved values
840         for info in ["git_info", "cvs_info", "svn_info"]:
841             if info in p_info:
842                 for key in p_info[info]:
843                     product_pyconf_cfg[p_info.section][info][key] = p_info[
844                                                                       info][key]
845     else:
846         # if the product is not archive, then make it become archive.
847         if src.product.product_is_vcs(p_info):
848             product_pyconf_cfg[p_info.section].get_source = "archive"
849             if not "archive_info" in product_pyconf_cfg[p_info.section]:
850                 product_pyconf_cfg[p_info.section].addMapping("archive_info",
851                                         src.pyconf.Mapping(product_pyconf_cfg),
852                                         "")
853             product_pyconf_cfg[p_info.section
854                               ].archive_info.archive_name = p_info.name + ".tgz"
855     
856     # write the pyconf file to the temporary project location
857     product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
858                                            p_name + ".pyconf")
859     ff = open(product_tmp_pyconf_path, 'w')
860     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
861     product_pyconf_cfg.__save__(ff, 1)
862     ff.close()
863
864 def find_application_pyconf(config, application_tmp_dir):
865     '''Find the application pyconf file and put it in the specific temporary 
866        directory containing the specific project of a source package.
867
868     :param config Config: The global configuration.
869     :param application_tmp_dir str: The path to the temporary application 
870                                        scripts directory of the project.
871     '''
872     # read the pyconf of the application
873     application_name = config.VARS.application
874     application_pyconf_path = src.find_file_in_lpath(
875                                             application_name + ".pyconf",
876                                             config.PATHS.APPLICATIONPATH)
877     application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
878     
879     # Change the workdir
880     application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
881                                     application_pyconf_cfg,
882                                     src.pyconf.DOLLAR,
883                                     'VARS.salometoolsway + $VARS.sep + ".."')
884
885     # Prevent from compilation in base
886     application_pyconf_cfg.APPLICATION.no_base = "yes"
887     
888     # write the pyconf file to the temporary application location
889     application_tmp_pyconf_path = os.path.join(application_tmp_dir,
890                                                application_name + ".pyconf")
891     ff = open(application_tmp_pyconf_path, 'w')
892     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
893     application_pyconf_cfg.__save__(ff, 1)
894     ff.close()
895
896 def project_package(project_file_path, tmp_working_dir):
897     '''Prepare a dictionary that stores all the needed directories and files to
898        add in a project package.
899     
900     :param project_file_path str: The path to the local project.
901     :param tmp_working_dir str: The temporary local directory containing some 
902                                 specific directories or files needed in the 
903                                 project package
904     :return: the dictionary that stores all the needed directories and files to
905              add in a project package.
906              {label : (path_on_local_machine, path_in_archive)}
907     :rtype: dict
908     '''
909     d_project = {}
910     # Read the project file and get the directories to add to the package
911     project_pyconf_cfg = src.pyconf.Config(project_file_path)
912     paths = {"ARCHIVEPATH" : "archives",
913              "APPLICATIONPATH" : "applications",
914              "PRODUCTPATH" : "products",
915              "JOBPATH" : "jobs",
916              "MACHINEPATH" : "machines"}
917     # Loop over the project paths and add it
918     for path in paths:
919         if path not in project_pyconf_cfg:
920             continue
921         # Add the directory to the files to add in the package
922         d_project[path] = (project_pyconf_cfg[path], paths[path])
923         # Modify the value of the path in the package
924         project_pyconf_cfg[path] = src.pyconf.Reference(
925                                     project_pyconf_cfg,
926                                     src.pyconf.DOLLAR,
927                                     'project_path + "/' + paths[path] + '"')
928     
929     # Modify some values
930     if "project_path" not in project_pyconf_cfg:
931         project_pyconf_cfg.addMapping("project_path",
932                                       src.pyconf.Mapping(project_pyconf_cfg),
933                                       "")
934     project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
935                                                            src.pyconf.DOLLAR,
936                                                            'PWD')
937     
938     # Write the project pyconf file
939     project_file_name = os.path.basename(project_file_path)
940     project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
941     ff = open(project_pyconf_tmp_path, 'w')
942     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
943     project_pyconf_cfg.__save__(ff, 1)
944     ff.close()
945     d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
946     
947     return d_project
948
949 def add_readme(config, options, where):
950     readme_path = os.path.join(where, "README")
951     with codecs.open(readme_path, "w", 'utf-8') as f:
952
953     # templates for building the header
954         readme_header="""
955 # This package was generated with sat $version
956 # Date: $date
957 # User: $user
958 # Distribution : $dist
959
960 In the following, $$ROOT represents the directory where you have installed 
961 SALOME (the directory where this file is located).
962
963 """
964         readme_compilation_with_binaries="""
965
966 compilation based on the binaries used as prerequisites
967 =======================================================
968
969 If you fail to compile the the complete application (for example because
970 you are not root on your system and cannot install missing packages), you
971 may try a partial compilation based on the binaries.
972 For that it is necessary to copy the binaries from BINARIES to INSTALL,
973 and do some substitutions on cmake and .la files (replace the build directories
974 with local paths).
975 The procedure to do it is:
976  1) Remove or rename INSTALL directory if it exists
977  2) Execute the shell script bin_install.sh:
978  > cd $ROOT
979  > ./bin_install.sh
980  3) Use SalomeTool (as explained in Sources section) and compile only the 
981     modules you need to (with -p option)
982
983 """
984         readme_header_tpl=string.Template(readme_header)
985         readme_template_path_bin_prof = os.path.join(config.VARS.internal_dir,
986                 "README_BIN.template")
987         readme_template_path_bin_noprof = os.path.join(config.VARS.internal_dir,
988                 "README_BIN_NO_PROFILE.template")
989         readme_template_path_src = os.path.join(config.VARS.internal_dir,
990                 "README_SRC.template")
991         readme_template_path_pro = os.path.join(config.VARS.internal_dir,
992                 "README_PROJECT.template")
993         readme_template_path_sat = os.path.join(config.VARS.internal_dir,
994                 "README_SAT.template")
995
996         # prepare substitution dictionary
997         d = dict()
998         d['user'] = config.VARS.user
999         d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
1000         d['version'] = config.INTERNAL.sat_version
1001         d['dist'] = config.VARS.dist
1002         f.write(readme_header_tpl.substitute(d)) # write the general header (common)
1003
1004         if options.binaries or options.sources:
1005             d['application'] = config.VARS.application
1006             f.write("# Application: " + d['application'])
1007             if 'profile' in config.APPLICATION:
1008                 d['launcher'] = config.APPLICATION.profile.launcher_name
1009                 d['launcher'] = config.APPLICATION.profile.launcher_name
1010             else:
1011                 d['env_file'] = 'env_launch.sh'
1012
1013         # write the specific sections
1014         if options.binaries:
1015             if "env_file" in d:
1016                 f.write(src.template.substitute(readme_template_path_bin_noprof, d))
1017             else:
1018                 f.write(src.template.substitute(readme_template_path_bin_prof, d))
1019
1020         if options.sources:
1021             f.write(src.template.substitute(readme_template_path_src, d))
1022
1023         if options.binaries and options.sources:
1024             f.write(readme_compilation_with_binaries)
1025
1026         if options.project:
1027             f.write(src.template.substitute(readme_template_path_pro, d))
1028
1029         if options.sat:
1030             f.write(src.template.substitute(readme_template_path_sat, d))
1031     
1032     return readme_path
1033
1034 def update_config(config, prop, value):
1035     '''Remove from config.APPLICATION.products the products that have the property given as input.
1036     
1037     :param config Config: The global config.
1038     :param prop str: The property to filter
1039     :param value str: The value of the property to filter
1040     '''
1041     src.check_config_has_application(config)
1042     l_product_to_remove = []
1043     for product_name in config.APPLICATION.products.keys():
1044         prod_cfg = src.product.get_product_config(config, product_name)
1045         if src.get_property_in_product_cfg(prod_cfg, prop) == value:
1046             l_product_to_remove.append(product_name)
1047     for product_name in l_product_to_remove:
1048         config.APPLICATION.products.__delitem__(product_name)
1049
1050 def description():
1051     '''method that is called when salomeTools is called with --help option.
1052     
1053     :return: The text to display for the package command description.
1054     :rtype: str
1055     '''
1056     return _("The package command creates an archive.\nThere are 4 kinds of "
1057              "archive, which can be mixed:\n  1- The binary archive. It contains all the product "
1058              "installation directories and a launcher,\n  2- The sources archive."
1059              " It contains the products archives, a project corresponding to "
1060              "the application and salomeTools,\n  3- The project archive. It "
1061              "contains a project (give the project file path as argument),\n  4-"
1062              " The salomeTools archive. It contains salomeTools.\n\nexample:"
1063              "\nsat package SALOME-master --bineries --sources")
1064   
1065 def run(args, runner, logger):
1066     '''method that is called when salomeTools is called with package parameter.
1067     '''
1068     
1069     # Parse the options
1070     (options, args) = parser.parse_args(args)
1071        
1072     # Check that a type of package is called, and only one
1073     all_option_types = (options.binaries,
1074                         options.sources,
1075                         options.project not in ["", None],
1076                         options.sat)
1077
1078     # Check if no option for package type
1079     if all_option_types.count(True) == 0:
1080         msg = _("Error: Precise a type for the package\nUse one of the "
1081                 "following options: --binaries, --sources, --project or"
1082                 " --salometools")
1083         logger.write(src.printcolors.printcError(msg), 1)
1084         logger.write("\n", 1)
1085         return 1
1086     
1087     # The repository where to put the package if not Binary or Source
1088     package_default_path = runner.cfg.USER.workdir
1089     
1090     # if the package contains binaries or sources:
1091     if options.binaries or options.sources:
1092         # Check that the command has been called with an application
1093         src.check_config_has_application(runner.cfg)
1094
1095         # Display information
1096         logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
1097                                                     runner.cfg.VARS.application), 1)
1098         
1099         # Get the default directory where to put the packages
1100         package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
1101                                             "PACKAGE")
1102         src.ensure_path_exists(package_default_path)
1103         
1104     # if the package contains a project:
1105     if options.project:
1106         # check that the project is visible by SAT
1107         if options.project not in runner.cfg.PROJECTS.project_file_paths:
1108             site_path = os.path.join(runner.cfg.VARS.salometoolsway,
1109                                      "data",
1110                                      "site.pyconf")
1111             msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
1112                     "\nPlease add it in the %(site)s file." % {
1113                                   "proj" : options.project, "site" : site_path})
1114             logger.write(src.printcolors.printcError(msg), 1)
1115             logger.write("\n", 1)
1116             return 1
1117     
1118     # Remove the products that are filtered by the --without_property option
1119     if options.without_property:
1120         [prop, value] = options.without_property.split(":")
1121         update_config(runner.cfg, prop, value)
1122     
1123     # get the name of the archive or build it
1124     if options.name:
1125         if os.path.basename(options.name) == options.name:
1126             # only a name (not a path)
1127             archive_name = options.name           
1128             dir_name = package_default_path
1129         else:
1130             archive_name = os.path.basename(options.name)
1131             dir_name = os.path.dirname(options.name)
1132         
1133         # suppress extension
1134         if archive_name[-len(".tgz"):] == ".tgz":
1135             archive_name = archive_name[:-len(".tgz")]
1136         if archive_name[-len(".tar.gz"):] == ".tar.gz":
1137             archive_name = archive_name[:-len(".tar.gz")]
1138         
1139     else:
1140         archive_name=""
1141         dir_name = package_default_path
1142         if options.binaries or options.sources:
1143             archive_name = runner.cfg.APPLICATION.name
1144
1145         if options.binaries:
1146             archive_name += "_"+runner.cfg.VARS.dist
1147             
1148         if options.sources:
1149             archive_name += "_SRC"
1150             if options.with_vcs:
1151                 archive_name += "_VCS"
1152
1153         if options.project:
1154             project_name, __ = os.path.splitext(
1155                                             os.path.basename(options.project))
1156             archive_name += ("PROJECT_" + project_name)
1157  
1158         if options.sat:
1159             archive_name += ("salomeTools_" + runner.cfg.INTERNAL.sat_version)
1160         if len(archive_name)==0: # no option worked 
1161             msg = _("Error: Cannot name the archive\n"
1162                     " check if at least one of the following options was "
1163                     "selected : --binaries, --sources, --project or"
1164                     " --salometools")
1165             logger.write(src.printcolors.printcError(msg), 1)
1166             logger.write("\n", 1)
1167             return 1
1168  
1169     path_targz = os.path.join(dir_name, archive_name + ".tgz")
1170     
1171     src.printcolors.print_value(logger, "Package path", path_targz, 2)
1172
1173     # Create a working directory for all files that are produced during the
1174     # package creation and that will be removed at the end of the command
1175     tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
1176                                    runner.cfg.VARS.datehour)
1177     src.ensure_path_exists(tmp_working_dir)
1178     logger.write("\n", 5)
1179     logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
1180     
1181     logger.write("\n", 3)
1182
1183     msg = _("Preparation of files to add to the archive")
1184     logger.write(src.printcolors.printcLabel(msg), 2)
1185     logger.write("\n", 2)
1186
1187     d_files_to_add={}  # content of the archive
1188
1189     # a dict to hold paths that will need to be substitute for users recompilations
1190     d_paths_to_substitute={}  
1191
1192     if options.binaries:
1193         d_bin_files_to_add = binary_package(runner.cfg,
1194                                             logger,
1195                                             options,
1196                                             tmp_working_dir)
1197         # for all binaries dir, store the substitution that will be required 
1198         # for extra compilations
1199         for key in d_bin_files_to_add:
1200             if key.endswith("(bin)"):
1201                 source_dir = d_bin_files_to_add[key][0]
1202                 path_in_archive = d_bin_files_to_add[key][1].replace("BINARIES-" + runner.cfg.VARS.dist,"INSTALL")
1203                 if os.path.basename(source_dir)==os.path.basename(path_in_archive):
1204                     # if basename is the same we will just substitute the dirname 
1205                     d_paths_to_substitute[os.path.dirname(source_dir)]=\
1206                         os.path.dirname(path_in_archive)
1207                 else:
1208                     d_paths_to_substitute[source_dir]=path_in_archive
1209
1210         d_files_to_add.update(d_bin_files_to_add)
1211
1212     if options.sources:
1213         d_files_to_add.update(source_package(runner,
1214                                         runner.cfg,
1215                                         logger, 
1216                                         options,
1217                                         tmp_working_dir))
1218         if options.binaries:
1219             # for archives with bin and sources we provide a shell script able to 
1220             # install binaries for compilation
1221             file_install_bin=produce_install_bin_file(runner.cfg,logger,
1222                                                       tmp_working_dir,
1223                                                       d_paths_to_substitute,
1224                                                       "install_bin.sh")
1225             d_files_to_add.update({"install_bin" : (file_install_bin, "install_bin.sh")})
1226             logger.write("substitutions that need to be done later : \n", 5)
1227             logger.write(str(d_paths_to_substitute), 5)
1228             logger.write("\n", 5)
1229     else:
1230         # --salomeTool option is not considered when --sources is selected, as this option
1231         # already brings salomeTool!
1232         if options.sat:
1233             d_files_to_add.update({"salomeTools" : (runner.cfg.VARS.salometoolsway, "")})
1234         
1235     
1236     if options.project:
1237         d_files_to_add.update(project_package(options.project, tmp_working_dir))
1238
1239     if not(d_files_to_add):
1240         msg = _("Error: Empty dictionnary to build the archive!\n")
1241         logger.write(src.printcolors.printcError(msg), 1)
1242         logger.write("\n", 1)
1243         return 1
1244
1245     # Add the README file in the package
1246     local_readme_tmp_path = add_readme(runner.cfg,
1247                                        options,
1248                                        tmp_working_dir)
1249     d_files_to_add["README"] = (local_readme_tmp_path, "README")
1250
1251     # Add the additional files of option add_files
1252     if options.add_files:
1253         for file_path in options.add_files:
1254             if not os.path.exists(file_path):
1255                 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1256                 continue
1257             file_name = os.path.basename(file_path)
1258             d_files_to_add[file_name] = (file_path, file_name)
1259
1260     logger.write("\n", 2)
1261
1262     logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1263     logger.write("\n", 2)
1264     
1265     try:
1266         # Creating the object tarfile
1267         tar = tarfile.open(path_targz, mode='w:gz')
1268         
1269         # get the filtering function if needed
1270         filter_function = None
1271         filter_function = exclude_VCS_and_extensions
1272
1273         # Add the files to the tarfile object
1274         res = add_files(tar, archive_name, d_files_to_add, logger, f_exclude=filter_function)
1275         tar.close()
1276     except KeyboardInterrupt:
1277         logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1278         logger.write(_("Removing the temporary working directory ... "), 1)
1279         # remove the working directory
1280         shutil.rmtree(tmp_working_dir)
1281         logger.write(_("OK"), 1)
1282         logger.write(_("\n"), 1)
1283         return 1
1284     
1285     # remove the working directory    
1286     shutil.rmtree(tmp_working_dir)
1287     
1288     # Print again the path of the package
1289     logger.write("\n", 2)
1290     src.printcolors.print_value(logger, "Package path", path_targz, 2)
1291     
1292     return res