Salome HOME
sat package: remove the .pyconf file in the root directory of
[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     # Remove the .pyconf file in the root directory of salomeTools if there is
688     # any. (For example when launching jobs, a pyconf file describing the jobs 
689     # can be here and is not useful) 
690     files_or_dir_SAT = os.listdir(os.path.join(tmp_working_dir, "salomeTools"))
691     for file_or_dir in files_or_dir_SAT:
692         if file_or_dir.endswith(".pyconf"):
693             file_path = os.path.join(tmp_working_dir,
694                                      "salomeTools",
695                                      file_or_dir)
696             os.remove(file_path)
697     
698     ff = open(local_pyconf_file, "w")
699     ff.write(LOCAL_TEMPLATE)
700     ff.close()
701     
702     return sat_tmp_path.path
703
704 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
705     '''For sources package that require that all products are get using an 
706        archive, one has to create some archive for the vcs products.
707        So this method calls the clean and source command of sat and then create
708        the archives.
709
710     :param l_pinfo_vcs List: The list of specific configuration corresponding to
711                              each vcs product
712     :param sat Sat: The Sat instance that can be called to clean and source the
713                     products
714     :param config Config: The global configuration.
715     :param logger Logger: the logging instance
716     :param tmp_working_dir str: The temporary local directory containing some 
717                                 specific directories or files needed in the 
718                                 source package
719     :return: the dictionary that stores all the archives to add in the source 
720              package. {label : (path_on_local_machine, path_in_archive)}
721     :rtype: dict
722     '''
723     # clean the source directory of all the vcs products, then use the source 
724     # command and thus construct an archive that will not contain the patches
725     l_prod_names = [pn for pn, __ in l_pinfo_vcs]
726     # clean
727     logger.write(_("clean sources\n"))
728     args_clean = config.VARS.application
729     args_clean += " --sources --products "
730     args_clean += ",".join(l_prod_names)
731     sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
732     # source
733     logger.write(_("get sources"))
734     args_source = config.VARS.application
735     args_source += " --products "
736     args_source += ",".join(l_prod_names)
737     sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
738
739     # make the new archives
740     d_archives_vcs = {}
741     for pn, pinfo in l_pinfo_vcs:
742         path_archive = make_archive(pn, pinfo, tmp_working_dir)
743         d_archives_vcs[pn] = (path_archive,
744                               os.path.join(ARCHIVE_DIR, pn + ".tgz"))
745     return d_archives_vcs
746
747 def make_archive(prod_name, prod_info, where):
748     '''Create an archive of a product by searching its source directory.
749
750     :param prod_name str: The name of the product.
751     :param prod_info Config: The specific configuration corresponding to the 
752                              product
753     :param where str: The path of the repository where to put the resulting 
754                       archive
755     :return: The path of the resulting archive
756     :rtype: str
757     '''
758     path_targz_prod = os.path.join(where, prod_name + ".tgz")
759     tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
760     local_path = prod_info.source_dir
761     tar_prod.add(local_path,
762                  arcname=prod_name,
763                  exclude=exclude_VCS_and_extensions)
764     tar_prod.close()
765     return path_targz_prod       
766
767 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
768     '''Create a specific project for a source package.
769
770     :param config Config: The global configuration.
771     :param tmp_working_dir str: The temporary local directory containing some 
772                                 specific directories or files needed in the 
773                                 source package
774     :param with_vcs boolean: True if the package is with vcs products (not 
775                              transformed into archive products)
776     :return: The dictionary 
777              {"project" : (produced project, project path in the archive)}
778     :rtype: Dict
779     '''
780
781     # Create in the working temporary directory the full project tree
782     project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
783     products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
784                                          "products")
785     compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
786                                          "products",
787                                          "compil_scripts")
788     env_scripts_tmp_dir = os.path.join(project_tmp_dir,
789                                          "products",
790                                          "env_scripts")
791     patches_tmp_dir = os.path.join(project_tmp_dir,
792                                          "products",
793                                          "patches")
794     application_tmp_dir = os.path.join(project_tmp_dir,
795                                          "applications")
796     for directory in [project_tmp_dir,
797                       compil_scripts_tmp_dir,
798                       env_scripts_tmp_dir,
799                       patches_tmp_dir,
800                       application_tmp_dir]:
801         src.ensure_path_exists(directory)
802
803     # Create the pyconf that contains the information of the project
804     project_pyconf_name = "project.pyconf"        
805     project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
806     ff = open(project_pyconf_file, "w")
807     ff.write(PROJECT_TEMPLATE)
808     ff.close()
809     
810     # Loop over the products to get there pyconf and all the scripts 
811     # (compilation, environment, patches)
812     # and create the pyconf file to add to the project
813     lproducts_name = config.APPLICATION.products.keys()
814     l_products = src.product.get_products_infos(lproducts_name, config)
815     for p_name, p_info in l_products:
816         find_product_scripts_and_pyconf(p_name,
817                                         p_info,
818                                         config,
819                                         with_vcs,
820                                         compil_scripts_tmp_dir,
821                                         env_scripts_tmp_dir,
822                                         patches_tmp_dir,
823                                         products_pyconf_tmp_dir)
824     
825     find_application_pyconf(config, application_tmp_dir)
826     
827     d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
828     return d_project
829
830 def find_product_scripts_and_pyconf(p_name,
831                                     p_info,
832                                     config,
833                                     with_vcs,
834                                     compil_scripts_tmp_dir,
835                                     env_scripts_tmp_dir,
836                                     patches_tmp_dir,
837                                     products_pyconf_tmp_dir):
838     '''Create a specific pyconf file for a given product. Get its environment 
839        script, its compilation script and patches and put it in the temporary
840        working directory. This method is used in the source package in order to
841        construct the specific project.
842
843     :param p_name str: The name of the product.
844     :param p_info Config: The specific configuration corresponding to the 
845                              product
846     :param config Config: The global configuration.
847     :param with_vcs boolean: True if the package is with vcs products (not 
848                              transformed into archive products)
849     :param compil_scripts_tmp_dir str: The path to the temporary compilation 
850                                        scripts directory of the project.
851     :param env_scripts_tmp_dir str: The path to the temporary environment script 
852                                     directory of the project.
853     :param patches_tmp_dir str: The path to the temporary patch scripts 
854                                 directory of the project.
855     :param products_pyconf_tmp_dir str: The path to the temporary product 
856                                         scripts directory of the project.
857     '''
858     
859     # read the pyconf of the product
860     product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
861                                            config.PATHS.PRODUCTPATH)
862     product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
863
864     # find the compilation script if any
865     if src.product.product_has_script(p_info):
866         compil_script_path = src.Path(p_info.compil_script)
867         compil_script_path.copy(compil_scripts_tmp_dir)
868         product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
869                                                     p_info.compil_script)
870     # find the environment script if any
871     if src.product.product_has_env_script(p_info):
872         env_script_path = src.Path(p_info.environ.env_script)
873         env_script_path.copy(env_scripts_tmp_dir)
874         product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
875                                                 p_info.environ.env_script)
876     # find the patches if any
877     if src.product.product_has_patches(p_info):
878         patches = src.pyconf.Sequence()
879         for patch_path in p_info.patches:
880             p_path = src.Path(patch_path)
881             p_path.copy(patches_tmp_dir)
882             patches.append(os.path.basename(patch_path), "")
883
884         product_pyconf_cfg[p_info.section].patches = patches
885     
886     if with_vcs:
887         # put in the pyconf file the resolved values
888         for info in ["git_info", "cvs_info", "svn_info"]:
889             if info in p_info:
890                 for key in p_info[info]:
891                     product_pyconf_cfg[p_info.section][info][key] = p_info[
892                                                                       info][key]
893     else:
894         # if the product is not archive, then make it become archive.
895         if src.product.product_is_vcs(p_info):
896             product_pyconf_cfg[p_info.section].get_source = "archive"
897             if not "archive_info" in product_pyconf_cfg[p_info.section]:
898                 product_pyconf_cfg[p_info.section].addMapping("archive_info",
899                                         src.pyconf.Mapping(product_pyconf_cfg),
900                                         "")
901             product_pyconf_cfg[p_info.section
902                               ].archive_info.archive_name = p_info.name + ".tgz"
903     
904     # write the pyconf file to the temporary project location
905     product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
906                                            p_name + ".pyconf")
907     ff = open(product_tmp_pyconf_path, 'w')
908     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
909     product_pyconf_cfg.__save__(ff, 1)
910     ff.close()
911
912 def find_application_pyconf(config, application_tmp_dir):
913     '''Find the application pyconf file and put it in the specific temporary 
914        directory containing the specific project of a source package.
915
916     :param config Config: The global configuration.
917     :param application_tmp_dir str: The path to the temporary application 
918                                        scripts directory of the project.
919     '''
920     # read the pyconf of the application
921     application_name = config.VARS.application
922     application_pyconf_path = src.find_file_in_lpath(
923                                             application_name + ".pyconf",
924                                             config.PATHS.APPLICATIONPATH)
925     application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
926     
927     # Change the workdir
928     application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
929                                     application_pyconf_cfg,
930                                     src.pyconf.DOLLAR,
931                                     'VARS.salometoolsway + $VARS.sep + ".."')
932
933     # Prevent from compilation in base
934     application_pyconf_cfg.APPLICATION.no_base = "yes"
935     
936     # write the pyconf file to the temporary application location
937     application_tmp_pyconf_path = os.path.join(application_tmp_dir,
938                                                application_name + ".pyconf")
939     ff = open(application_tmp_pyconf_path, 'w')
940     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
941     application_pyconf_cfg.__save__(ff, 1)
942     ff.close()
943
944 def project_package(project_file_path, tmp_working_dir):
945     '''Prepare a dictionary that stores all the needed directories and files to
946        add in a project package.
947     
948     :param project_file_path str: The path to the local project.
949     :param tmp_working_dir str: The temporary local directory containing some 
950                                 specific directories or files needed in the 
951                                 project package
952     :return: the dictionary that stores all the needed directories and files to
953              add in a project package.
954              {label : (path_on_local_machine, path_in_archive)}
955     :rtype: dict
956     '''
957     d_project = {}
958     # Read the project file and get the directories to add to the package
959     project_pyconf_cfg = src.pyconf.Config(project_file_path)
960     paths = {"ARCHIVEPATH" : "archives",
961              "APPLICATIONPATH" : "applications",
962              "PRODUCTPATH" : "products",
963              "JOBPATH" : "jobs",
964              "MACHINEPATH" : "machines"}
965     # Loop over the project paths and add it
966     for path in paths:
967         if path not in project_pyconf_cfg:
968             continue
969         # Add the directory to the files to add in the package
970         d_project[path] = (project_pyconf_cfg[path], paths[path])
971         # Modify the value of the path in the package
972         project_pyconf_cfg[path] = src.pyconf.Reference(
973                                     project_pyconf_cfg,
974                                     src.pyconf.DOLLAR,
975                                     'project_path + "/' + paths[path] + '"')
976     
977     # Modify some values
978     if "project_path" not in project_pyconf_cfg:
979         project_pyconf_cfg.addMapping("project_path",
980                                       src.pyconf.Mapping(project_pyconf_cfg),
981                                       "")
982     project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
983                                                            src.pyconf.DOLLAR,
984                                                            'PWD')
985     
986     # Write the project pyconf file
987     project_file_name = os.path.basename(project_file_path)
988     project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
989     ff = open(project_pyconf_tmp_path, 'w')
990     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
991     project_pyconf_cfg.__save__(ff, 1)
992     ff.close()
993     d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
994     
995     return d_project
996
997 def add_readme(config, options, where):
998     readme_path = os.path.join(where, "README")
999     with codecs.open(readme_path, "w", 'utf-8') as f:
1000
1001     # templates for building the header
1002         readme_header="""
1003 # This package was generated with sat $version
1004 # Date: $date
1005 # User: $user
1006 # Distribution : $dist
1007
1008 In the following, $$ROOT represents the directory where you have installed 
1009 SALOME (the directory where this file is located).
1010
1011 """
1012         readme_compilation_with_binaries="""
1013
1014 compilation based on the binaries used as prerequisites
1015 =======================================================
1016
1017 If you fail to compile the complete application (for example because
1018 you are not root on your system and cannot install missing packages), you
1019 may try a partial compilation based on the binaries.
1020 For that it is necessary to copy the binaries from BINARIES to INSTALL,
1021 and do some substitutions on cmake and .la files (replace the build directories
1022 with local paths).
1023 The procedure to do it is:
1024  1) Remove or rename INSTALL directory if it exists
1025  2) Execute the shell script install_bin.sh:
1026  > cd $ROOT
1027  > ./install_bin.sh
1028  3) Use SalomeTool (as explained in Sources section) and compile only the 
1029     modules you need to (with -p option)
1030
1031 """
1032         readme_header_tpl=string.Template(readme_header)
1033         readme_template_path_bin_prof = os.path.join(config.VARS.internal_dir,
1034                 "README_BIN.template")
1035         readme_template_path_bin_noprof = os.path.join(config.VARS.internal_dir,
1036                 "README_BIN_NO_PROFILE.template")
1037         readme_template_path_src = os.path.join(config.VARS.internal_dir,
1038                 "README_SRC.template")
1039         readme_template_path_pro = os.path.join(config.VARS.internal_dir,
1040                 "README_PROJECT.template")
1041         readme_template_path_sat = os.path.join(config.VARS.internal_dir,
1042                 "README_SAT.template")
1043
1044         # prepare substitution dictionary
1045         d = dict()
1046         d['user'] = config.VARS.user
1047         d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
1048         d['version'] = config.INTERNAL.sat_version
1049         d['dist'] = config.VARS.dist
1050         f.write(readme_header_tpl.substitute(d)) # write the general header (common)
1051
1052         if options.binaries or options.sources:
1053             d['application'] = config.VARS.application
1054             f.write("# Application: " + d['application'])
1055             if 'profile' in config.APPLICATION:
1056                 d['launcher'] = config.APPLICATION.profile.launcher_name
1057                 d['launcher'] = config.APPLICATION.profile.launcher_name
1058             else:
1059                 d['env_file'] = 'env_launch.sh'
1060
1061         # write the specific sections
1062         if options.binaries:
1063             if "env_file" in d:
1064                 f.write(src.template.substitute(readme_template_path_bin_noprof, d))
1065             else:
1066                 f.write(src.template.substitute(readme_template_path_bin_prof, d))
1067
1068         if options.sources:
1069             f.write(src.template.substitute(readme_template_path_src, d))
1070
1071         if options.binaries and options.sources:
1072             f.write(readme_compilation_with_binaries)
1073
1074         if options.project:
1075             f.write(src.template.substitute(readme_template_path_pro, d))
1076
1077         if options.sat:
1078             f.write(src.template.substitute(readme_template_path_sat, d))
1079     
1080     return readme_path
1081
1082 def update_config(config, prop, value):
1083     '''Remove from config.APPLICATION.products the products that have the property given as input.
1084     
1085     :param config Config: The global config.
1086     :param prop str: The property to filter
1087     :param value str: The value of the property to filter
1088     '''
1089     src.check_config_has_application(config)
1090     l_product_to_remove = []
1091     for product_name in config.APPLICATION.products.keys():
1092         prod_cfg = src.product.get_product_config(config, product_name)
1093         if src.get_property_in_product_cfg(prod_cfg, prop) == value:
1094             l_product_to_remove.append(product_name)
1095     for product_name in l_product_to_remove:
1096         config.APPLICATION.products.__delitem__(product_name)
1097
1098 def description():
1099     '''method that is called when salomeTools is called with --help option.
1100     
1101     :return: The text to display for the package command description.
1102     :rtype: str
1103     '''
1104     return _("The package command creates an archive.\nThere are 4 kinds of "
1105              "archive, which can be mixed:\n  1- The binary archive. It contains all the product "
1106              "installation directories and a launcher,\n  2- The sources archive."
1107              " It contains the products archives, a project corresponding to "
1108              "the application and salomeTools,\n  3- The project archive. It "
1109              "contains a project (give the project file path as argument),\n  4-"
1110              " The salomeTools archive. It contains salomeTools.\n\nexample:"
1111              "\nsat package SALOME-master --bineries --sources")
1112   
1113 def run(args, runner, logger):
1114     '''method that is called when salomeTools is called with package parameter.
1115     '''
1116     
1117     # Parse the options
1118     (options, args) = parser.parse_args(args)
1119        
1120     # Check that a type of package is called, and only one
1121     all_option_types = (options.binaries,
1122                         options.sources,
1123                         options.project not in ["", None],
1124                         options.sat)
1125
1126     # Check if no option for package type
1127     if all_option_types.count(True) == 0:
1128         msg = _("Error: Precise a type for the package\nUse one of the "
1129                 "following options: --binaries, --sources, --project or"
1130                 " --salometools")
1131         logger.write(src.printcolors.printcError(msg), 1)
1132         logger.write("\n", 1)
1133         return 1
1134     
1135     # The repository where to put the package if not Binary or Source
1136     package_default_path = runner.cfg.USER.workdir
1137     
1138     # if the package contains binaries or sources:
1139     if options.binaries or options.sources:
1140         # Check that the command has been called with an application
1141         src.check_config_has_application(runner.cfg)
1142
1143         # Display information
1144         logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
1145                                                     runner.cfg.VARS.application), 1)
1146         
1147         # Get the default directory where to put the packages
1148         package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
1149                                             "PACKAGE")
1150         src.ensure_path_exists(package_default_path)
1151         
1152     # if the package contains a project:
1153     if options.project:
1154         # check that the project is visible by SAT
1155         if options.project not in runner.cfg.PROJECTS.project_file_paths:
1156             local_path = os.path.join(runner.cfg.VARS.salometoolsway,
1157                                      "data",
1158                                      "local.pyconf")
1159             msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
1160                     "\nPlease add it in the %(local)s file." % {
1161                                   "proj" : options.project, "local" : local_path})
1162             logger.write(src.printcolors.printcError(msg), 1)
1163             logger.write("\n", 1)
1164             return 1
1165     
1166     # Remove the products that are filtered by the --without_property option
1167     if options.without_property:
1168         [prop, value] = options.without_property.split(":")
1169         update_config(runner.cfg, prop, value)
1170     
1171     # get the name of the archive or build it
1172     if options.name:
1173         if os.path.basename(options.name) == options.name:
1174             # only a name (not a path)
1175             archive_name = options.name           
1176             dir_name = package_default_path
1177         else:
1178             archive_name = os.path.basename(options.name)
1179             dir_name = os.path.dirname(options.name)
1180         
1181         # suppress extension
1182         if archive_name[-len(".tgz"):] == ".tgz":
1183             archive_name = archive_name[:-len(".tgz")]
1184         if archive_name[-len(".tar.gz"):] == ".tar.gz":
1185             archive_name = archive_name[:-len(".tar.gz")]
1186         
1187     else:
1188         archive_name=""
1189         dir_name = package_default_path
1190         if options.binaries or options.sources:
1191             archive_name = runner.cfg.APPLICATION.name
1192
1193         if options.binaries:
1194             archive_name += "_"+runner.cfg.VARS.dist
1195             
1196         if options.sources:
1197             archive_name += "_SRC"
1198             if options.with_vcs:
1199                 archive_name += "_VCS"
1200
1201         if options.project:
1202             project_name, __ = os.path.splitext(
1203                                             os.path.basename(options.project))
1204             archive_name += ("PROJECT_" + project_name)
1205  
1206         if options.sat:
1207             archive_name += ("salomeTools_" + runner.cfg.INTERNAL.sat_version)
1208         if len(archive_name)==0: # no option worked 
1209             msg = _("Error: Cannot name the archive\n"
1210                     " check if at least one of the following options was "
1211                     "selected : --binaries, --sources, --project or"
1212                     " --salometools")
1213             logger.write(src.printcolors.printcError(msg), 1)
1214             logger.write("\n", 1)
1215             return 1
1216  
1217     path_targz = os.path.join(dir_name, archive_name + ".tgz")
1218     
1219     src.printcolors.print_value(logger, "Package path", path_targz, 2)
1220
1221     # Create a working directory for all files that are produced during the
1222     # package creation and that will be removed at the end of the command
1223     tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
1224                                    runner.cfg.VARS.datehour)
1225     src.ensure_path_exists(tmp_working_dir)
1226     logger.write("\n", 5)
1227     logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
1228     
1229     logger.write("\n", 3)
1230
1231     msg = _("Preparation of files to add to the archive")
1232     logger.write(src.printcolors.printcLabel(msg), 2)
1233     logger.write("\n", 2)
1234
1235     d_files_to_add={}  # content of the archive
1236
1237     # a dict to hold paths that will need to be substitute for users recompilations
1238     d_paths_to_substitute={}  
1239
1240     if options.binaries:
1241         d_bin_files_to_add = binary_package(runner.cfg,
1242                                             logger,
1243                                             options,
1244                                             tmp_working_dir)
1245         # for all binaries dir, store the substitution that will be required 
1246         # for extra compilations
1247         for key in d_bin_files_to_add:
1248             if key.endswith("(bin)"):
1249                 source_dir = d_bin_files_to_add[key][0]
1250                 path_in_archive = d_bin_files_to_add[key][1].replace("BINARIES-" + runner.cfg.VARS.dist,"INSTALL")
1251                 if os.path.basename(source_dir)==os.path.basename(path_in_archive):
1252                     # if basename is the same we will just substitute the dirname 
1253                     d_paths_to_substitute[os.path.dirname(source_dir)]=\
1254                         os.path.dirname(path_in_archive)
1255                 else:
1256                     d_paths_to_substitute[source_dir]=path_in_archive
1257
1258         d_files_to_add.update(d_bin_files_to_add)
1259
1260     if options.sources:
1261         d_files_to_add.update(source_package(runner,
1262                                         runner.cfg,
1263                                         logger, 
1264                                         options,
1265                                         tmp_working_dir))
1266         if options.binaries:
1267             # for archives with bin and sources we provide a shell script able to 
1268             # install binaries for compilation
1269             file_install_bin=produce_install_bin_file(runner.cfg,logger,
1270                                                       tmp_working_dir,
1271                                                       d_paths_to_substitute,
1272                                                       "install_bin.sh")
1273             d_files_to_add.update({"install_bin" : (file_install_bin, "install_bin.sh")})
1274             logger.write("substitutions that need to be done later : \n", 5)
1275             logger.write(str(d_paths_to_substitute), 5)
1276             logger.write("\n", 5)
1277     else:
1278         # --salomeTool option is not considered when --sources is selected, as this option
1279         # already brings salomeTool!
1280         if options.sat:
1281             d_files_to_add.update({"salomeTools" : (runner.cfg.VARS.salometoolsway, "")})
1282         
1283     
1284     if options.project:
1285         d_files_to_add.update(project_package(options.project, tmp_working_dir))
1286
1287     if not(d_files_to_add):
1288         msg = _("Error: Empty dictionnary to build the archive!\n")
1289         logger.write(src.printcolors.printcError(msg), 1)
1290         logger.write("\n", 1)
1291         return 1
1292
1293     # Add the README file in the package
1294     local_readme_tmp_path = add_readme(runner.cfg,
1295                                        options,
1296                                        tmp_working_dir)
1297     d_files_to_add["README"] = (local_readme_tmp_path, "README")
1298
1299     # Add the additional files of option add_files
1300     if options.add_files:
1301         for file_path in options.add_files:
1302             if not os.path.exists(file_path):
1303                 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1304                 continue
1305             file_name = os.path.basename(file_path)
1306             d_files_to_add[file_name] = (file_path, file_name)
1307
1308     logger.write("\n", 2)
1309
1310     logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1311     logger.write("\n", 2)
1312     
1313     try:
1314         # Creating the object tarfile
1315         tar = tarfile.open(path_targz, mode='w:gz')
1316         
1317         # get the filtering function if needed
1318         filter_function = exclude_VCS_and_extensions
1319
1320         # Add the files to the tarfile object
1321         res = add_files(tar, archive_name, d_files_to_add, logger, f_exclude=filter_function)
1322         tar.close()
1323     except KeyboardInterrupt:
1324         logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1325         logger.write(_("Removing the temporary working directory ... "), 1)
1326         # remove the working directory
1327         shutil.rmtree(tmp_working_dir)
1328         logger.write(_("OK"), 1)
1329         logger.write(_("\n"), 1)
1330         return 1
1331     
1332     # remove the working directory    
1333     shutil.rmtree(tmp_working_dir)
1334     
1335     # Print again the path of the package
1336     logger.write("\n", 2)
1337     src.printcolors.print_value(logger, "Package path", path_targz, 2)
1338     
1339     return res