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