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