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