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