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