]> SALOME platform Git repositories - tools/sat.git/blob - commands/package.py
Salome HOME
Fix bug for package command
[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 tarfile
23
24 import src
25
26 BINARY = "binary"
27 SOURCE = "Source"
28 PROJECT = "Project"
29 SAT = "Sat"
30
31 ARCHIVE_DIR = "ARCHIVES"
32 PROJECT_DIR = "PROJECT"
33
34 PROJECT_TEMPLATE = """#!/usr/bin/env python
35 #-*- coding:utf-8 -*-
36
37 # The path to the archive root directory
38 root_path : $PWD + "/../"
39 # path to the PROJECT
40 project_path : $PWD + "/"
41
42 # Where to search the archives of the products
43 ARCHIVEPATH : $root_path + "ARCHIVES"
44 # Where to search the pyconf of the applications
45 APPLICATIONPATH : $project_path + "applications/"
46 # Where to search the pyconf of the products
47 PRODUCTPATH : $project_path + "products/"
48 # Where to search the pyconf of the jobs of the project
49 JOBPATH : $project_path + "jobs/"
50 # Where to search the pyconf of the machines of the project
51 MACHINEPATH : $project_path + "machines/"
52 """
53
54 SITE_TEMPLATE = ("""#!/usr/bin/env python
55 #-*- coding:utf-8 -*-
56
57 SITE :
58 {   
59     log :
60     {
61         log_dir : $USER.workdir + "/LOGS"
62     }
63     test :{
64            tmp_dir_with_application : '/tmp' + $VARS.sep + $VARS.user + """
65 """$VARS.sep + $APPLICATION.name + $VARS.sep + 'test'
66            tmp_dir : '/tmp' + $VARS.sep + $VARS.user + $VARS.sep + 'test'
67            timeout : 150
68            }
69 }
70
71 PROJECTS :
72 {
73 project_file_paths : [$VARS.salometoolsway + $VARS.sep + \"..\" + $VARS.sep"""
74 """ + \"""" + PROJECT_DIR + """\" + $VARS.sep + "project.pyconf"]
75 }
76 """)
77
78 # Define all possible option for the package command :  sat package <options>
79 parser = src.options.Options()
80 parser.add_option('b', 'binaries', 'boolean', 'binaries',
81     _('Produce a binary package.'), False)
82 parser.add_option('s', 'sources', 'boolean', 'sources',
83     _('Produce a compilable archive of the sources of the application.'), False)
84 parser.add_option('p', 'project', 'string', 'project',
85     _('Produce an archive that contains a project.'), "")
86 parser.add_option('', 'salometools', 'boolean', 'sat',
87     _('Produce an archive that contains salomeTools.'), False)
88 parser.add_option('n', 'name', 'string', 'name',
89     _('The name or full path of the archive.'), None)
90 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
91     _('Only source package: do not make archive of vcs products.'), False)
92
93 def add_files(tar, name_archive, d_content, logger):
94     '''Create an archive containing all directories and files that are given in
95        the d_content argument.
96     
97     :param tar tarfile: The tarfile instance used to make the archive.
98     :param name_archive str: The name of the archive to make.
99     :param d_content dict: The dictionary that contain all directories and files
100                            to add in the archive.
101                            d_content[label] = 
102                                         (path_on_local_machine, path_in_archive)
103     :param logger Logger: the logging instance
104     :return: 0 if success, 1 if not.
105     :rtype: int
106     '''
107     # get the max length of the messages in order to make the display
108     max_len = len(max(d_content.keys(), key=len))
109     
110     success = 0
111     # loop over each directory or file stored in the d_content dictionary
112     for name in d_content.keys():
113         # display information
114         len_points = max_len - len(name)
115         logger.write(name + " " + len_points * "." + " ", 3)
116         # Get the local path and the path in archive 
117         # of the directory or file to add
118         local_path, archive_path = d_content[name]
119         in_archive = os.path.join(name_archive, archive_path)
120         # Add it in the archive
121         try:
122             tar.add(local_path, arcname=in_archive)
123             logger.write(src.printcolors.printcSuccess(_("OK")), 3)
124         except Exception as e:
125             logger.write(src.printcolors.printcError(_("KO ")), 3)
126             logger.write(str(e), 3)
127             success = 1
128         logger.write("\n", 3)
129     return success
130
131 def produce_relative_launcher(config,
132                               logger,
133                               file_dir,
134                               file_name,
135                               binaries_dir_name):
136     '''Create a specific SALOME launcher for the binary package. This launcher 
137        uses relative paths.
138     
139     :param config Config: The global configuration.
140     :param logger Logger: the logging instance
141     :param file_dir str: the directory where to put the launcher
142     :param file_name str: The launcher name
143     :param binaries_dir_name str: the name of the repository where the binaries
144                                   are, in the archive.
145     :return: the path of the produced launcher
146     :rtype: str
147     '''
148     
149     # Get the launcher template
150     profile_install_dir = os.path.join(binaries_dir_name,
151                                        config.APPLICATION.profile.product)
152     withProfile = src.fileEnviron.withProfile   
153     withProfile = withProfile.replace(
154         "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
155         "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '/" + profile_install_dir + "'")
156     withProfile = withProfile.replace(
157         "os.path.join( 'PROFILE_INSTALL_DIR'",
158         "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
159
160     before, after = withProfile.split(
161                                 "# here your local standalone environment\n")
162
163     # create an environment file writer
164     writer = src.environment.FileEnvWriter(config,
165                                            logger,
166                                            file_dir,
167                                            src_root=None)
168     
169     filepath = os.path.join(file_dir, file_name)
170     # open the file and write into it
171     launch_file = open(filepath, "w")
172     launch_file.write(before)
173     # Write
174     writer.write_cfgForPy_file(launch_file, for_package = binaries_dir_name)
175     launch_file.write(after)
176     launch_file.close()
177     
178     # Little hack to put out_dir_Path outside the strings
179     src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
180     
181     # change the rights in order to make the file executable for everybody
182     os.chmod(filepath,
183              stat.S_IRUSR |
184              stat.S_IRGRP |
185              stat.S_IROTH |
186              stat.S_IWUSR |
187              stat.S_IXUSR |
188              stat.S_IXGRP |
189              stat.S_IXOTH)
190     
191     return filepath
192
193 def binary_package(config, logger, options, tmp_working_dir):
194     '''Prepare a dictionary that stores all the needed directories and files to
195        add in a binary package.
196     
197     :param config Config: The global configuration.
198     :param logger Logger: the logging instance
199     :param options OptResult: the options of the launched command
200     :param tmp_working_dir str: The temporary local directory containing some 
201                                 specific directories or files needed in the 
202                                 binary package
203     :return: the dictionary that stores all the needed directories and files to
204              add in a binary package.
205              {label : (path_on_local_machine, path_in_archive)}
206     :rtype: dict
207     '''
208
209     # Get the list of product installation to add to the archive
210     l_products_name = config.APPLICATION.products.keys()
211     l_product_info = src.product.get_products_infos(l_products_name,
212                                                     config)
213     l_install_dir = []
214     l_not_installed = []
215     for prod_name, prod_info in l_product_info:
216         # ignore the native and fixed products
217         if (src.product.product_is_native(prod_info) 
218                 or src.product.product_is_fixed(prod_info)):
219             continue
220         if src.product.check_installation(prod_info):
221             l_install_dir.append((prod_name, prod_info.install_dir))
222         else:
223             l_not_installed.append(prod_name)
224     
225     # Print warning or error if there are some missing products
226     if len(l_not_installed) > 0:
227         text_missing_prods = ""
228         for p_name in l_not_installed:
229             text_missing_prods += "-" + p_name + "\n"
230         if not options.force_creation:
231             msg = _("ERROR: there are missing products installations:")
232             logger.write("%s\n%s" % (src.printcolors.printcError(msg),
233                                      text_missing_prods),
234                          1)
235             return None
236         else:
237             msg = _("WARNING: there are missing products installations:")
238             logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
239                                      text_missing_prods),
240                          1)
241     
242     # construct the name of the directory that will contain the binaries
243     binaries_dir_name = "BINARIES-" + config.VARS.dist
244     
245     # construct the correlation table between the product names, there 
246     # actual install directories and there install directory in archive
247     d_products = {}
248     for prod_name, install_dir in l_install_dir:
249         path_in_archive = os.path.join(binaries_dir_name, prod_name)
250         d_products[prod_name] = (install_dir, path_in_archive)
251     
252     # create the relative launcher and add it to the files to add
253     launcher_name = config.APPLICATION.profile.launcher_name
254     launcher_package = produce_relative_launcher(config,
255                                                  logger,
256                                                  tmp_working_dir,
257                                                  launcher_name,
258                                                  binaries_dir_name)
259     
260     d_products["launcher"] = (launcher_package, launcher_name)
261     
262     return d_products
263
264 def source_package(sat, config, logger, options, tmp_working_dir):
265     '''Prepare a dictionary that stores all the needed directories and files to
266        add in a source package.
267     
268     :param config Config: The global configuration.
269     :param logger Logger: the logging instance
270     :param options OptResult: the options of the launched command
271     :param tmp_working_dir str: The temporary local directory containing some 
272                                 specific directories or files needed in the 
273                                 binary package
274     :return: the dictionary that stores all the needed directories and files to
275              add in a source package.
276              {label : (path_on_local_machine, path_in_archive)}
277     :rtype: dict
278     '''
279     
280     # Get all the products that are prepared using an archive
281     logger.write("Find archive products ... ")
282     d_archives, l_pinfo_vcs = get_archives(config, logger)
283     logger.write("Done\n")
284     d_archives_vcs = {}
285     if not options.with_vcs:
286         # Make archives with the products that are not prepared using an archive
287         # (git, cvs, svn, etc)
288         logger.write("Construct archives for vcs products ... ")
289         d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
290                                           sat,
291                                           config,
292                                           logger,
293                                           tmp_working_dir)
294         logger.write("Done\n")
295
296     # Create a project
297     logger.write("Create the project ... ")
298     d_project = create_project_for_src_package(config,
299                                                 tmp_working_dir,
300                                                 options.with_vcs)
301     logger.write("Done\n")
302     
303     # Add salomeTools
304     tmp_sat = add_salomeTools(config, tmp_working_dir)
305     d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
306     
307     # Add a sat symbolic link
308     tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
309     t = os.getcwd()
310     os.chdir(tmp_working_dir)
311     if os.path.lexists(tmp_satlink_path):
312         os.remove(tmp_satlink_path)
313     os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
314     os.chdir(t)
315     
316     d_sat["sat link"] = (tmp_satlink_path, "sat")
317     
318     return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
319
320 def get_archives(config, logger):
321     '''Find all the products that are get using an archive and all the products
322        that are get using a vcs (git, cvs, svn) repository.
323     
324     :param config Config: The global configuration.
325     :param logger Logger: the logging instance
326     :return: the dictionary {name_product : 
327              (local path of its archive, path in the package of its archive )}
328              and the list of specific configuration corresponding to the vcs 
329              products
330     :rtype: (Dict, List)
331     '''
332     # Get the list of product informations
333     l_products_name = config.APPLICATION.products.keys()
334     l_product_info = src.product.get_products_infos(l_products_name,
335                                                     config)
336     d_archives = {}
337     l_pinfo_vcs = []
338     for p_name, p_info in l_product_info:
339         # ignore the native and fixed products
340         if (src.product.product_is_native(p_info) 
341                 or src.product.product_is_fixed(p_info)):
342             continue
343         if p_info.get_source == "archive":
344             archive_path = p_info.archive_info.archive_name
345             archive_name = os.path.basename(archive_path)
346         else:
347             l_pinfo_vcs.append((p_name, p_info))
348             
349         d_archives[p_name] = (archive_path,
350                               os.path.join(ARCHIVE_DIR, archive_name))
351     return d_archives, l_pinfo_vcs
352
353 def add_salomeTools(config, tmp_working_dir):
354     '''Prepare a version of salomeTools that has a specific site.pyconf file 
355        configured for a source package.
356
357     :param config Config: The global configuration.
358     :param tmp_working_dir str: The temporary local directory containing some 
359                                 specific directories or files needed in the 
360                                 source package
361     :return: The path to the local salomeTools directory to add in the package
362     :rtype: str
363     '''
364     # Copy sat in the temporary working directory
365     sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
366     sat_running_path = src.Path(config.VARS.salometoolsway)
367     sat_running_path.copy(sat_tmp_path)
368     
369     # Update the site.pyconf file that contains the path to the project
370     site_pyconf_name = "site.pyconf"
371     site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
372     site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
373     ff = open(site_pyconf_file, "w")
374     ff.write(SITE_TEMPLATE)
375     ff.close()
376     
377     return sat_tmp_path.path
378
379 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
380     '''For sources package that require that all products are get using an 
381        archive, one has to create some archive for the vcs products.
382        So this method calls the clean and source command of sat and then create
383        the archives.
384
385     :param l_pinfo_vcs List: The list of specific configuration corresponding to
386                              each vcs product
387     :param sat Sat: The Sat instance that can be called to clean and source the
388                     products
389     :param config Config: The global configuration.
390     :param logger Logger: the logging instance
391     :param tmp_working_dir str: The temporary local directory containing some 
392                                 specific directories or files needed in the 
393                                 source package
394     :return: the dictionary that stores all the archives to add in the source 
395              package. {label : (path_on_local_machine, path_in_archive)}
396     :rtype: dict
397     '''
398     # clean the source directory of all the vcs products, then use the source 
399     # command and thus construct an archive that will not contain the patches
400     l_prod_names = [pn for pn, __ in l_pinfo_vcs]
401     # clean
402     logger.write(_("clean sources\n"))
403     args_clean = config.VARS.application
404     args_clean += " --sources --products "
405     args_clean += ",".join(l_prod_names)
406     sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
407     # source
408     logger.write(_("get sources"))
409     args_source = config.VARS.application
410     args_source += " --products "
411     args_source += ",".join(l_prod_names)
412     sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
413
414     # make the new archives
415     d_archives_vcs = {}
416     for pn, pinfo in l_pinfo_vcs:
417         path_archive = make_archive(pn, pinfo, tmp_working_dir)
418         d_archives_vcs[pn] = (path_archive,
419                               os.path.join(ARCHIVE_DIR, pn + ".tgz"))
420     return d_archives_vcs
421
422 def make_archive(prod_name, prod_info, where):
423     '''Create an archive of a product by searching its source directory.
424
425     :param prod_name str: The name of the product.
426     :param prod_info Config: The specific configuration corresponding to the 
427                              product
428     :param where str: The path of the repository where to put the resulting 
429                       archive
430     :return: The path of the resulting archive
431     :rtype: str
432     '''
433     path_targz_prod = os.path.join(where, prod_name + ".tgz")
434     tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
435     local_path = prod_info.source_dir
436     tar_prod.add(local_path, arcname=prod_name)
437     tar_prod.close()
438     return path_targz_prod       
439
440 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
441     '''Create a specific project for a source package.
442
443     :param config Config: The global configuration.
444     :param tmp_working_dir str: The temporary local directory containing some 
445                                 specific directories or files needed in the 
446                                 source package
447     :param with_vcs boolean: True if the package is with vcs products (not 
448                              transformed into archive products)
449     :return: The dictionary 
450              {"project" : (produced project, project path in the archive)}
451     :rtype: Dict
452     '''
453
454     # Create in the working temporary directory the full project tree
455     project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
456     products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
457                                          "products")
458     compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
459                                          "products",
460                                          "compil_scripts")
461     env_scripts_tmp_dir = os.path.join(project_tmp_dir,
462                                          "products",
463                                          "env_scripts")
464     patches_tmp_dir = os.path.join(project_tmp_dir,
465                                          "products",
466                                          "patches")
467     application_tmp_dir = os.path.join(project_tmp_dir,
468                                          "applications")
469     for directory in [project_tmp_dir,
470                       compil_scripts_tmp_dir,
471                       env_scripts_tmp_dir,
472                       patches_tmp_dir,
473                       application_tmp_dir]:
474         src.ensure_path_exists(directory)
475
476     # Create the pyconf that contains the information of the project
477     project_pyconf_name = "project.pyconf"        
478     project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
479     ff = open(project_pyconf_file, "w")
480     ff.write(PROJECT_TEMPLATE)
481     ff.close()
482     
483     # Loop over the products to get there pyconf and all the scripts 
484     # (compilation, environment, patches)
485     # and create the pyconf file to add to the project
486     lproducts_name = config.APPLICATION.products.keys()
487     l_products = src.product.get_products_infos(lproducts_name, config)
488     for p_name, p_info in l_products:
489         # ignore native and fixed products
490         if (src.product.product_is_native(p_info) or 
491                 src.product.product_is_fixed(p_info)):
492             continue
493         find_product_scripts_and_pyconf(p_name,
494                                         p_info,
495                                         config,
496                                         with_vcs,
497                                         compil_scripts_tmp_dir,
498                                         env_scripts_tmp_dir,
499                                         patches_tmp_dir,
500                                         products_pyconf_tmp_dir)
501     
502     find_application_pyconf(config, application_tmp_dir)
503     
504     d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
505     return d_project
506
507 def find_product_scripts_and_pyconf(p_name,
508                                     p_info,
509                                     config,
510                                     with_vcs,
511                                     compil_scripts_tmp_dir,
512                                     env_scripts_tmp_dir,
513                                     patches_tmp_dir,
514                                     products_pyconf_tmp_dir):
515     '''Create a specific pyconf file for a given product. Get its environment 
516        script, its compilation script and patches and put it in the temporary
517        working directory. This method is used in the source package in order to
518        construct the specific project.
519
520     :param p_name str: The name of the product.
521     :param p_info Config: The specific configuration corresponding to the 
522                              product
523     :param config Config: The global configuration.
524     :param with_vcs boolean: True if the package is with vcs products (not 
525                              transformed into archive products)
526     :param compil_scripts_tmp_dir str: The path to the temporary compilation 
527                                        scripts directory of the project.
528     :param env_scripts_tmp_dir str: The path to the temporary environment script 
529                                     directory of the project.
530     :param patches_tmp_dir str: The path to the temporary patch scripts 
531                                 directory of the project.
532     :param products_pyconf_tmp_dir str: The path to the temporary product 
533                                         scripts directory of the project.
534     '''
535     
536     # read the pyconf of the product
537     product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
538                                            config.PATHS.PRODUCTPATH)
539     product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
540
541     # find the compilation script if any
542     if src.product.product_has_script(p_info):
543         compil_script_path = src.Path(p_info.compil_script)
544         compil_script_path.copy(compil_scripts_tmp_dir)
545         product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
546                                                     p_info.compil_script)
547     # find the environment script if any
548     if src.product.product_has_env_script(p_info):
549         env_script_path = src.Path(p_info.environ.env_script)
550         env_script_path.copy(env_scripts_tmp_dir)
551         product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
552                                                 p_info.environ.env_script)
553     # find the patches if any
554     if src.product.product_has_patches(p_info):
555         patches = src.pyconf.Sequence()
556         for patch_path in p_info.patches:
557             p_path = src.Path(patch_path)
558             p_path.copy(patches_tmp_dir)
559             patches.append(os.path.basename(patch_path), "")
560
561         product_pyconf_cfg[p_info.section].patches = patches
562     
563     if with_vcs:
564         # put in the pyconf file the resolved values
565         for info in ["git_info", "cvs_info", "svn_info"]:
566             if info in p_info:
567                 for key in p_info[info]:
568                     product_pyconf_cfg[p_info.section][info][key] = p_info[
569                                                                       info][key]
570     else:
571         # if the product is not archive, then make it become archive.
572         if src.product.product_is_vcs(p_info):
573             product_pyconf_cfg[p_info.section].get_source = "archive"
574             if not "archive_info" in product_pyconf_cfg[p_info.section]:
575                 product_pyconf_cfg[p_info.section].addMapping("archive_info",
576                                         src.pyconf.Mapping(product_pyconf_cfg),
577                                         "")
578             product_pyconf_cfg[p_info.section
579                               ].archive_info.archive_name = p_info.name + ".tgz"
580     
581     # write the pyconf file to the temporary project location
582     product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
583                                            p_name + ".pyconf")
584     ff = open(product_tmp_pyconf_path, 'w')
585     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
586     product_pyconf_cfg.__save__(ff, 1)
587     ff.close()
588
589 def find_application_pyconf(config, application_tmp_dir):
590     '''Find the application pyconf file and put it in the specific temporary 
591        directory containing the specific project of a source package.
592
593     :param config Config: The global configuration.
594     :param application_tmp_dir str: The path to the temporary application 
595                                        scripts directory of the project.
596     '''
597     # read the pyconf of the application
598     application_name = config.VARS.application
599     application_pyconf_path = src.find_file_in_lpath(
600                                             application_name + ".pyconf",
601                                             config.PATHS.APPLICATIONPATH)
602     application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
603     
604     # Change the workdir
605     application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
606                                     application_pyconf_cfg,
607                                     src.pyconf.DOLLAR,
608                                     'VARS.salometoolsway + $VARS.sep + ".."')
609
610     # Prevent from compilation in base
611     application_pyconf_cfg.APPLICATION.no_base = "yes"
612     
613     # write the pyconf file to the temporary application location
614     application_tmp_pyconf_path = os.path.join(application_tmp_dir,
615                                                application_name + ".pyconf")
616     ff = open(application_tmp_pyconf_path, 'w')
617     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
618     application_pyconf_cfg.__save__(ff, 1)
619     ff.close()
620
621 def project_package(project_file_path, tmp_working_dir):
622     '''Prepare a dictionary that stores all the needed directories and files to
623        add in a project package.
624     
625     :param project_file_path str: The path to the local project.
626     :param tmp_working_dir str: The temporary local directory containing some 
627                                 specific directories or files needed in the 
628                                 project package
629     :return: the dictionary that stores all the needed directories and files to
630              add in a project package.
631              {label : (path_on_local_machine, path_in_archive)}
632     :rtype: dict
633     '''
634     d_project = {}
635     # Read the project file and get the directories to add to the package
636     project_pyconf_cfg = src.pyconf.Config(project_file_path)
637     paths = {"ARCHIVEPATH" : "archives",
638              "APPLICATIONPATH" : "applications",
639              "PRODUCTPATH" : "products",
640              "JOBPATH" : "jobs",
641              "MACHINEPATH" : "machines"}
642     # Loop over the project paths and add it
643     for path in paths:
644         if path not in project_pyconf_cfg:
645             continue
646         # Add the directory to the files to add in the package
647         d_project[path] = (project_pyconf_cfg[path], paths[path])
648         # Modify the value of the path in the package
649         project_pyconf_cfg[path] = src.pyconf.Reference(
650                                     project_pyconf_cfg,
651                                     src.pyconf.DOLLAR,
652                                     'project_path + "/' + paths[path] + '"')
653     
654     # Modify some values
655     if "project_path" not in project_pyconf_cfg:
656         project_pyconf_cfg.addMapping("project_path",
657                                       src.pyconf.Mapping(project_pyconf_cfg),
658                                       "")
659     project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
660                                                            src.pyconf.DOLLAR,
661                                                            'PWD')
662     
663     # Write the project pyconf file
664     project_file_name = os.path.basename(project_file_path)
665     project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
666     ff = open(project_pyconf_tmp_path, 'w')
667     ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
668     project_pyconf_cfg.__save__(ff, 1)
669     ff.close()
670     d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
671     
672     return d_project
673
674 def description():
675     '''method that is called when salomeTools is called with --help option.
676     
677     :return: The text to display for the package command description.
678     :rtype: str
679     '''
680     return _("The package command creates an archive.\nThere are 4 kinds of "
681              "archive:\n  1- The binary archive. It contains all the product "
682              "installation directories and a launcher,\n  2- The sources archive."
683              " It contains the products archives, a project corresponding to "
684              "the application and salomeTools,\n  3- The project archive. It "
685              "contains a project (give the project file path as argument),\n  4-"
686              " The salomeTools archive. It contains salomeTools.")
687   
688 def run(args, runner, logger):
689     '''method that is called when salomeTools is called with package parameter.
690     '''
691     
692     # Parse the options
693     (options, args) = parser.parse_args(args)
694        
695     # Check that a type of package is called, and only one
696     all_option_types = (options.binaries,
697                         options.sources,
698                         options.project != "",
699                         options.sat)
700
701     # Check if no option for package type
702     if all_option_types.count(True) == 0:
703         msg = _("Error: Precise a type for the package\nUse one of the "
704                 "following options: --binaries, --sources, --project or --sat")
705         logger.write(src.printcolors.printcError(msg), 1)
706         logger.write("\n", 1)
707         return 1
708     
709     # Check for only one option for package type
710     if all_option_types.count(True) > 1:
711         msg = _("Error: You can use only one type for the package\nUse only one"
712                 " of the following options: --binaries, --sources, --project or"
713                 " --sat")
714         logger.write(src.printcolors.printcError(msg), 1)
715         logger.write("\n", 1)
716         return 1
717     
718     # Get the package type
719     if options.binaries:
720         package_type = BINARY
721     if options.sources:
722         package_type = SOURCE
723     if options.project:
724         package_type = PROJECT
725     if options.sat:
726         package_type = SAT
727
728     # The repository where to put the package if not Binary or Source
729     package_default_path = runner.cfg.USER.workdir
730     
731     if package_type in [BINARY, SOURCE]:
732         # Check that the command has been called with an application
733         src.check_config_has_application(runner.cfg)
734
735         # Display information
736         logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
737                                                     runner.cfg.VARS.application), 1)
738         
739         # Get the default directory where to put the packages
740         package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
741                                             "PACKAGE")
742         src.ensure_path_exists(package_default_path)
743         
744     elif package_type == PROJECT:
745         # check that the project is visible by SAT
746         if options.project not in runner.cfg.PROJECTS.project_file_paths:
747             site_path = os.path.join(runner.cfg.VARS.salometoolsway,
748                                      "data",
749                                      "site.pyconf")
750             msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
751                     "\nPlease add it in the %(site)s file." % {
752                                   "proj" : options.project, "site" : site_path})
753             logger.write(src.printcolors.printcError(msg), 1)
754             logger.write("\n", 1)
755             return 1
756     
757     # Print
758     src.printcolors.print_value(logger, "Package type", package_type, 2)
759
760     # get the name of the archive or construct it
761     if options.name:
762         if os.path.basename(options.name) == options.name:
763             # only a name (not a path)
764             archive_name = options.name           
765             dir_name = package_default_path
766         else:
767             archive_name = os.path.basename(options.name)
768             dir_name = os.path.dirname(options.name)
769         
770         # suppress extension
771         if archive_name[-len(".tgz"):] == ".tgz":
772             archive_name = archive_name[:-len(".tgz")]
773         if archive_name[-len(".tar.gz"):] == ".tar.gz":
774             archive_name = archive_name[:-len(".tar.gz")]
775         
776     else:
777         dir_name = package_default_path
778         if package_type == BINARY:
779             archive_name = (runner.cfg.APPLICATION.name +
780                             "-" +
781                             runner.cfg.VARS.dist)
782             
783         if package_type == SOURCE:
784             archive_name = (runner.cfg.APPLICATION.name +
785                             "-" +
786                             "SRC")
787
788         if package_type == PROJECT:
789             project_name, __ = os.path.splitext(
790                                             os.path.basename(options.project))
791             archive_name = ("PROJECT" +
792                             "-" +
793                             project_name)
794  
795         if package_type == SAT:
796             archive_name = ("salomeTools" +
797                             "-" +
798                             runner.cfg.INTERNAL.sat_version)
799  
800     path_targz = os.path.join(dir_name, archive_name + ".tgz")
801     
802     # Print the path of the package
803     src.printcolors.print_value(logger, "Package path", path_targz, 2)
804
805     # Create a working directory for all files that are produced during the
806     # package creation and that will be removed at the end of the command
807     tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
808                                    runner.cfg.VARS.datehour)
809     src.ensure_path_exists(tmp_working_dir)
810     
811     logger.write("\n", 3)
812
813     msg = _("Preparation of files to add to the archive")
814     logger.write(src.printcolors.printcLabel(msg), 2)
815     logger.write("\n", 2)
816
817     if package_type == BINARY:
818         d_files_to_add = binary_package(runner.cfg,
819                                         logger,
820                                         options,
821                                         tmp_working_dir)
822         if not(d_files_to_add):
823             return 1
824
825     if package_type == SOURCE:
826         d_files_to_add = source_package(runner,
827                                         runner.cfg,
828                                         logger, 
829                                         options,
830                                         tmp_working_dir)          
831     
832     if package_type == PROJECT:
833         d_files_to_add = project_package(options.project, tmp_working_dir)
834
835     if package_type == SAT:
836         d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
837     
838     logger.write("\n", 2)
839
840     logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
841     logger.write("\n", 2)
842     
843     # Creating the object tarfile
844     tar = tarfile.open(path_targz, mode='w:gz')
845     
846     # Add the files to the tarfile object
847     res = add_files(tar, archive_name, d_files_to_add, logger)
848     tar.close()
849     
850     # remove the working directory
851     shutil.rmtree(tmp_working_dir)
852     
853     # Print again the path of the package
854     logger.write("\n", 2)
855     src.printcolors.print_value(logger, "Package path", path_targz, 2)
856     
857     return res