3 # Copyright (C) 2010-2012 CEA/DEN
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.
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.
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
32 ARCHIVE_DIR = "ARCHIVES"
33 PROJECT_DIR = "PROJECT"
35 PROJECT_TEMPLATE = """#!/usr/bin/env python
38 # The path to the archive root directory
39 root_path : $PWD + "/../"
41 project_path : $PWD + "/"
43 # Where to search the archives of the products
44 ARCHIVEPATH : $root_path + "ARCHIVES"
45 # Where to search the pyconf of the applications
46 APPLICATIONPATH : $project_path + "applications/"
47 # Where to search the pyconf of the products
48 PRODUCTPATH : $project_path + "products/"
49 # Where to search the pyconf of the jobs of the project
50 JOBPATH : $project_path + "jobs/"
51 # Where to search the pyconf of the machines of the project
52 MACHINEPATH : $project_path + "machines/"
55 SITE_TEMPLATE = ("""#!/usr/bin/env python
62 log_dir : $USER.workdir + "/LOGS"
65 tmp_dir_with_application : '/tmp' + $VARS.sep + $VARS.user + """
66 """$VARS.sep + $APPLICATION.name + $VARS.sep + 'test'
67 tmp_dir : '/tmp' + $VARS.sep + $VARS.user + $VARS.sep + 'test'
74 project_file_paths : [$VARS.salometoolsway + $VARS.sep + \"..\" + $VARS.sep"""
75 """ + \"""" + PROJECT_DIR + """\" + $VARS.sep + "project.pyconf"]
79 # Define all possible option for the package command : sat package <options>
80 parser = src.options.Options()
81 parser.add_option('b', 'binaries', 'boolean', 'binaries',
82 _('Optional: Produce a binary package.'), False)
83 parser.add_option('f', 'force_creation', 'boolean', 'force_creation',
84 _('Optional: Only binary package: produce the archive even if '
85 'there are some missing products.'), False)
86 parser.add_option('', 'with_sources', 'boolean', 'with_sources',
87 _('Optional: Only binary package: produce and and a source archive in the '
88 'binary package.'), False)
89 parser.add_option('s', 'sources', 'boolean', 'sources',
90 _('Optional: Produce a compilable archive of the sources of the '
91 'application.'), False)
92 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
93 _('Optional: Only source package: do not make archive of vcs products.'),
95 parser.add_option('p', 'project', 'string', 'project',
96 _('Optional: Produce an archive that contains a project.'), "")
97 parser.add_option('t', 'salometools', 'boolean', 'sat',
98 _('Optional: Produce an archive that contains salomeTools.'), False)
99 parser.add_option('n', 'name', 'string', 'name',
100 _('Optional: The name or full path of the archive.'), None)
102 def add_files(tar, name_archive, d_content, logger):
103 '''Create an archive containing all directories and files that are given in
104 the d_content argument.
106 :param tar tarfile: The tarfile instance used to make the archive.
107 :param name_archive str: The name of the archive to make.
108 :param d_content dict: The dictionary that contain all directories and files
109 to add in the archive.
111 (path_on_local_machine, path_in_archive)
112 :param logger Logger: the logging instance
113 :return: 0 if success, 1 if not.
116 # get the max length of the messages in order to make the display
117 max_len = len(max(d_content.keys(), key=len))
120 # loop over each directory or file stored in the d_content dictionary
121 for name in d_content.keys():
122 # display information
123 len_points = max_len - len(name)
124 logger.write(name + " " + len_points * "." + " ", 3)
125 # Get the local path and the path in archive
126 # of the directory or file to add
127 local_path, archive_path = d_content[name]
128 in_archive = os.path.join(name_archive, archive_path)
129 # Add it in the archive
131 tar.add(local_path, arcname=in_archive)
132 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
133 except Exception as e:
134 logger.write(src.printcolors.printcError(_("KO ")), 3)
135 logger.write(str(e), 3)
137 logger.write("\n", 3)
140 def produce_relative_launcher(config,
145 '''Create a specific SALOME launcher for the binary package. This launcher
148 :param config Config: The global configuration.
149 :param logger Logger: the logging instance
150 :param file_dir str: the directory where to put the launcher
151 :param file_name str: The launcher name
152 :param binaries_dir_name str: the name of the repository where the binaries
154 :return: the path of the produced launcher
158 # Get the launcher template
159 profile_install_dir = os.path.join(binaries_dir_name,
160 config.APPLICATION.profile.product)
161 withProfile = src.fileEnviron.withProfile
162 withProfile = withProfile.replace(
163 "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
164 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
165 withProfile = withProfile.replace(
166 "os.path.join( 'PROFILE_INSTALL_DIR'",
167 "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
169 before, after = withProfile.split(
170 "# here your local standalone environment\n")
172 # create an environment file writer
173 writer = src.environment.FileEnvWriter(config,
178 filepath = os.path.join(file_dir, file_name)
179 # open the file and write into it
180 launch_file = open(filepath, "w")
181 launch_file.write(before)
183 writer.write_cfgForPy_file(launch_file, for_package = binaries_dir_name)
184 launch_file.write(after)
187 # Little hack to put out_dir_Path outside the strings
188 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
190 # change the rights in order to make the file executable for everybody
202 def binary_package(config, logger, options, tmp_working_dir):
203 '''Prepare a dictionary that stores all the needed directories and files to
204 add in a binary package.
206 :param config Config: The global configuration.
207 :param logger Logger: the logging instance
208 :param options OptResult: the options of the launched command
209 :param tmp_working_dir str: The temporary local directory containing some
210 specific directories or files needed in the
212 :return: the dictionary that stores all the needed directories and files to
213 add in a binary package.
214 {label : (path_on_local_machine, path_in_archive)}
218 # Get the list of product installation to add to the archive
219 l_products_name = config.APPLICATION.products.keys()
220 l_product_info = src.product.get_products_infos(l_products_name,
224 for prod_name, prod_info in l_product_info:
225 # ignore the native and fixed products
226 if (src.product.product_is_native(prod_info)
227 or src.product.product_is_fixed(prod_info)
228 or not src.product.product_compiles(prod_info)):
230 if src.product.check_installation(prod_info):
231 l_install_dir.append((prod_name, prod_info.install_dir))
233 l_not_installed.append(prod_name)
235 # Print warning or error if there are some missing products
236 if len(l_not_installed) > 0:
237 text_missing_prods = ""
238 for p_name in l_not_installed:
239 text_missing_prods += "-" + p_name + "\n"
240 if not options.force_creation:
241 msg = _("ERROR: there are missing products installations:")
242 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
247 msg = _("WARNING: there are missing products installations:")
248 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
252 # construct the name of the directory that will contain the binaries
253 binaries_dir_name = "BINARIES-" + config.VARS.dist
255 # construct the correlation table between the product names, there
256 # actual install directories and there install directory in archive
258 for prod_name, install_dir in l_install_dir:
259 path_in_archive = os.path.join(binaries_dir_name, prod_name)
260 d_products[prod_name] = (install_dir, path_in_archive)
262 # create the relative launcher and add it to the files to add
263 if "profile" in config.APPLICATION:
264 launcher_name = config.APPLICATION.profile.launcher_name
265 launcher_package = produce_relative_launcher(config,
271 d_products["launcher"] = (launcher_package, launcher_name)
275 def source_package(sat, config, logger, options, tmp_working_dir):
276 '''Prepare a dictionary that stores all the needed directories and files to
277 add in a source package.
279 :param config Config: The global configuration.
280 :param logger Logger: the logging instance
281 :param options OptResult: the options of the launched command
282 :param tmp_working_dir str: The temporary local directory containing some
283 specific directories or files needed in the
285 :return: the dictionary that stores all the needed directories and files to
286 add in a source package.
287 {label : (path_on_local_machine, path_in_archive)}
291 # Get all the products that are prepared using an archive
292 logger.write("Find archive products ... ")
293 d_archives, l_pinfo_vcs = get_archives(config, logger)
294 logger.write("Done\n")
296 if not options.with_vcs and len(l_pinfo_vcs) > 0:
297 # Make archives with the products that are not prepared using an archive
298 # (git, cvs, svn, etc)
299 logger.write("Construct archives for vcs products ... ")
300 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
305 logger.write("Done\n")
308 logger.write("Create the project ... ")
309 d_project = create_project_for_src_package(config,
312 logger.write("Done\n")
315 tmp_sat = add_salomeTools(config, tmp_working_dir)
316 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
318 # Add a sat symbolic link
319 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
321 os.chdir(tmp_working_dir)
322 if os.path.lexists(tmp_satlink_path):
323 os.remove(tmp_satlink_path)
324 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
327 d_sat["sat link"] = (tmp_satlink_path, "sat")
329 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
331 def get_archives(config, logger):
332 '''Find all the products that are get using an archive and all the products
333 that are get using a vcs (git, cvs, svn) repository.
335 :param config Config: The global configuration.
336 :param logger Logger: the logging instance
337 :return: the dictionary {name_product :
338 (local path of its archive, path in the package of its archive )}
339 and the list of specific configuration corresponding to the vcs
343 # Get the list of product informations
344 l_products_name = config.APPLICATION.products.keys()
345 l_product_info = src.product.get_products_infos(l_products_name,
349 for p_name, p_info in l_product_info:
350 # ignore the native and fixed products
351 if (src.product.product_is_native(p_info)
352 or src.product.product_is_fixed(p_info)):
354 if p_info.get_source == "archive":
355 archive_path = p_info.archive_info.archive_name
356 archive_name = os.path.basename(archive_path)
358 l_pinfo_vcs.append((p_name, p_info))
360 d_archives[p_name] = (archive_path,
361 os.path.join(ARCHIVE_DIR, archive_name))
362 return d_archives, l_pinfo_vcs
364 def add_salomeTools(config, tmp_working_dir):
365 '''Prepare a version of salomeTools that has a specific site.pyconf file
366 configured for a source package.
368 :param config Config: The global configuration.
369 :param tmp_working_dir str: The temporary local directory containing some
370 specific directories or files needed in the
372 :return: The path to the local salomeTools directory to add in the package
375 # Copy sat in the temporary working directory
376 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
377 sat_running_path = src.Path(config.VARS.salometoolsway)
378 sat_running_path.copy(sat_tmp_path)
380 # Update the site.pyconf file that contains the path to the project
381 site_pyconf_name = "site.pyconf"
382 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
383 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
384 ff = open(site_pyconf_file, "w")
385 ff.write(SITE_TEMPLATE)
388 return sat_tmp_path.path
390 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
391 '''For sources package that require that all products are get using an
392 archive, one has to create some archive for the vcs products.
393 So this method calls the clean and source command of sat and then create
396 :param l_pinfo_vcs List: The list of specific configuration corresponding to
398 :param sat Sat: The Sat instance that can be called to clean and source the
400 :param config Config: The global configuration.
401 :param logger Logger: the logging instance
402 :param tmp_working_dir str: The temporary local directory containing some
403 specific directories or files needed in the
405 :return: the dictionary that stores all the archives to add in the source
406 package. {label : (path_on_local_machine, path_in_archive)}
409 # clean the source directory of all the vcs products, then use the source
410 # command and thus construct an archive that will not contain the patches
411 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
413 logger.write(_("clean sources\n"))
414 args_clean = config.VARS.application
415 args_clean += " --sources --products "
416 args_clean += ",".join(l_prod_names)
417 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
419 logger.write(_("get sources"))
420 args_source = config.VARS.application
421 args_source += " --products "
422 args_source += ",".join(l_prod_names)
423 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
425 # make the new archives
427 for pn, pinfo in l_pinfo_vcs:
428 path_archive = make_archive(pn, pinfo, tmp_working_dir)
429 d_archives_vcs[pn] = (path_archive,
430 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
431 return d_archives_vcs
433 def make_archive(prod_name, prod_info, where):
434 '''Create an archive of a product by searching its source directory.
436 :param prod_name str: The name of the product.
437 :param prod_info Config: The specific configuration corresponding to the
439 :param where str: The path of the repository where to put the resulting
441 :return: The path of the resulting archive
444 path_targz_prod = os.path.join(where, prod_name + ".tgz")
445 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
446 local_path = prod_info.source_dir
447 tar_prod.add(local_path, arcname=prod_name)
449 return path_targz_prod
451 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
452 '''Create a specific project for a source package.
454 :param config Config: The global configuration.
455 :param tmp_working_dir str: The temporary local directory containing some
456 specific directories or files needed in the
458 :param with_vcs boolean: True if the package is with vcs products (not
459 transformed into archive products)
460 :return: The dictionary
461 {"project" : (produced project, project path in the archive)}
465 # Create in the working temporary directory the full project tree
466 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
467 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
469 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
472 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
475 patches_tmp_dir = os.path.join(project_tmp_dir,
478 application_tmp_dir = os.path.join(project_tmp_dir,
480 for directory in [project_tmp_dir,
481 compil_scripts_tmp_dir,
484 application_tmp_dir]:
485 src.ensure_path_exists(directory)
487 # Create the pyconf that contains the information of the project
488 project_pyconf_name = "project.pyconf"
489 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
490 ff = open(project_pyconf_file, "w")
491 ff.write(PROJECT_TEMPLATE)
494 # Loop over the products to get there pyconf and all the scripts
495 # (compilation, environment, patches)
496 # and create the pyconf file to add to the project
497 lproducts_name = config.APPLICATION.products.keys()
498 l_products = src.product.get_products_infos(lproducts_name, config)
499 for p_name, p_info in l_products:
500 # ignore native and fixed products
501 if (src.product.product_is_native(p_info) or
502 src.product.product_is_fixed(p_info)):
504 find_product_scripts_and_pyconf(p_name,
508 compil_scripts_tmp_dir,
511 products_pyconf_tmp_dir)
513 find_application_pyconf(config, application_tmp_dir)
515 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
518 def find_product_scripts_and_pyconf(p_name,
522 compil_scripts_tmp_dir,
525 products_pyconf_tmp_dir):
526 '''Create a specific pyconf file for a given product. Get its environment
527 script, its compilation script and patches and put it in the temporary
528 working directory. This method is used in the source package in order to
529 construct the specific project.
531 :param p_name str: The name of the product.
532 :param p_info Config: The specific configuration corresponding to the
534 :param config Config: The global configuration.
535 :param with_vcs boolean: True if the package is with vcs products (not
536 transformed into archive products)
537 :param compil_scripts_tmp_dir str: The path to the temporary compilation
538 scripts directory of the project.
539 :param env_scripts_tmp_dir str: The path to the temporary environment script
540 directory of the project.
541 :param patches_tmp_dir str: The path to the temporary patch scripts
542 directory of the project.
543 :param products_pyconf_tmp_dir str: The path to the temporary product
544 scripts directory of the project.
547 # read the pyconf of the product
548 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
549 config.PATHS.PRODUCTPATH)
550 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
552 # find the compilation script if any
553 if src.product.product_has_script(p_info):
554 compil_script_path = src.Path(p_info.compil_script)
555 compil_script_path.copy(compil_scripts_tmp_dir)
556 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
557 p_info.compil_script)
558 # find the environment script if any
559 if src.product.product_has_env_script(p_info):
560 env_script_path = src.Path(p_info.environ.env_script)
561 env_script_path.copy(env_scripts_tmp_dir)
562 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
563 p_info.environ.env_script)
564 # find the patches if any
565 if src.product.product_has_patches(p_info):
566 patches = src.pyconf.Sequence()
567 for patch_path in p_info.patches:
568 p_path = src.Path(patch_path)
569 p_path.copy(patches_tmp_dir)
570 patches.append(os.path.basename(patch_path), "")
572 product_pyconf_cfg[p_info.section].patches = patches
575 # put in the pyconf file the resolved values
576 for info in ["git_info", "cvs_info", "svn_info"]:
578 for key in p_info[info]:
579 product_pyconf_cfg[p_info.section][info][key] = p_info[
582 # if the product is not archive, then make it become archive.
583 if src.product.product_is_vcs(p_info):
584 product_pyconf_cfg[p_info.section].get_source = "archive"
585 if not "archive_info" in product_pyconf_cfg[p_info.section]:
586 product_pyconf_cfg[p_info.section].addMapping("archive_info",
587 src.pyconf.Mapping(product_pyconf_cfg),
589 product_pyconf_cfg[p_info.section
590 ].archive_info.archive_name = p_info.name + ".tgz"
592 # write the pyconf file to the temporary project location
593 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
595 ff = open(product_tmp_pyconf_path, 'w')
596 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
597 product_pyconf_cfg.__save__(ff, 1)
600 def find_application_pyconf(config, application_tmp_dir):
601 '''Find the application pyconf file and put it in the specific temporary
602 directory containing the specific project of a source package.
604 :param config Config: The global configuration.
605 :param application_tmp_dir str: The path to the temporary application
606 scripts directory of the project.
608 # read the pyconf of the application
609 application_name = config.VARS.application
610 application_pyconf_path = src.find_file_in_lpath(
611 application_name + ".pyconf",
612 config.PATHS.APPLICATIONPATH)
613 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
616 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
617 application_pyconf_cfg,
619 'VARS.salometoolsway + $VARS.sep + ".."')
621 # Prevent from compilation in base
622 application_pyconf_cfg.APPLICATION.no_base = "yes"
624 # write the pyconf file to the temporary application location
625 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
626 application_name + ".pyconf")
627 ff = open(application_tmp_pyconf_path, 'w')
628 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
629 application_pyconf_cfg.__save__(ff, 1)
632 def project_package(project_file_path, tmp_working_dir):
633 '''Prepare a dictionary that stores all the needed directories and files to
634 add in a project package.
636 :param project_file_path str: The path to the local project.
637 :param tmp_working_dir str: The temporary local directory containing some
638 specific directories or files needed in the
640 :return: the dictionary that stores all the needed directories and files to
641 add in a project package.
642 {label : (path_on_local_machine, path_in_archive)}
646 # Read the project file and get the directories to add to the package
647 project_pyconf_cfg = src.pyconf.Config(project_file_path)
648 paths = {"ARCHIVEPATH" : "archives",
649 "APPLICATIONPATH" : "applications",
650 "PRODUCTPATH" : "products",
652 "MACHINEPATH" : "machines"}
653 # Loop over the project paths and add it
655 if path not in project_pyconf_cfg:
657 # Add the directory to the files to add in the package
658 d_project[path] = (project_pyconf_cfg[path], paths[path])
659 # Modify the value of the path in the package
660 project_pyconf_cfg[path] = src.pyconf.Reference(
663 'project_path + "/' + paths[path] + '"')
666 if "project_path" not in project_pyconf_cfg:
667 project_pyconf_cfg.addMapping("project_path",
668 src.pyconf.Mapping(project_pyconf_cfg),
670 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
674 # Write the project pyconf file
675 project_file_name = os.path.basename(project_file_path)
676 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
677 ff = open(project_pyconf_tmp_path, 'w')
678 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
679 project_pyconf_cfg.__save__(ff, 1)
681 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
685 def add_readme(config, package_type, where):
686 readme_path = os.path.join(where, "README")
687 f = open(readme_path, 'w')
688 # prepare substitution dictionary
690 if package_type == BINARY:
691 d['application'] = config.VARS.application
692 d['user'] = config.VARS.user
693 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
694 d['version'] = config.INTERNAL.sat_version
695 d['dist'] = config.VARS.dist
696 if 'profile' in config.APPLICATION:
697 d['launcher'] = config.APPLICATION.profile.launcher_name
698 readme_template_path = os.path.join(config.VARS.internal_dir,
699 "README_BIN.template")
700 if package_type == SOURCE:
701 d['application'] = config.VARS.application
702 d['user'] = config.VARS.user
703 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
704 d['version'] = config.INTERNAL.sat_version
705 if 'profile' in config.APPLICATION:
706 d['profile'] = config.APPLICATION.profile.product
707 d['launcher'] = config.APPLICATION.profile.launcher_name
708 readme_template_path = os.path.join(config.VARS.internal_dir,
709 "README_SRC.template")
711 if package_type == PROJECT:
712 d['user'] = config.VARS.user
713 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
714 d['version'] = config.INTERNAL.sat_version
715 readme_template_path = os.path.join(config.VARS.internal_dir,
716 "README_PROJECT.template")
718 if package_type == SAT:
719 d['user'] = config.VARS.user
720 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
721 d['version'] = config.INTERNAL.sat_version
722 readme_template_path = os.path.join(config.VARS.internal_dir,
723 "README_SAT.template")
725 f.write(src.template.substitute(readme_template_path, d))
731 '''method that is called when salomeTools is called with --help option.
733 :return: The text to display for the package command description.
736 return _("The package command creates an archive.\nThere are 4 kinds of "
737 "archive:\n 1- The binary archive. It contains all the product "
738 "installation directories and a launcher,\n 2- The sources archive."
739 " It contains the products archives, a project corresponding to "
740 "the application and salomeTools,\n 3- The project archive. It "
741 "contains a project (give the project file path as argument),\n 4-"
742 " The salomeTools archive. It contains salomeTools.\n\nexample:"
743 "\nsat package SALOME-master --sources")
745 def run(args, runner, logger):
746 '''method that is called when salomeTools is called with package parameter.
750 (options, args) = parser.parse_args(args)
752 # Check that a type of package is called, and only one
753 all_option_types = (options.binaries,
755 options.project not in ["", None],
758 # Check if no option for package type
759 if all_option_types.count(True) == 0:
760 msg = _("Error: Precise a type for the package\nUse one of the "
761 "following options: --binaries, --sources, --project or"
763 logger.write(src.printcolors.printcError(msg), 1)
764 logger.write("\n", 1)
767 # Check for only one option for package type
768 if all_option_types.count(True) > 1:
769 msg = _("Error: You can use only one type for the package\nUse only one"
770 " of the following options: --binaries, --sources, --project or"
772 logger.write(src.printcolors.printcError(msg), 1)
773 logger.write("\n", 1)
776 # Get the package type
778 package_type = BINARY
780 package_type = SOURCE
782 package_type = PROJECT
786 # The repository where to put the package if not Binary or Source
787 package_default_path = runner.cfg.USER.workdir
789 if package_type in [BINARY, SOURCE]:
790 # Check that the command has been called with an application
791 src.check_config_has_application(runner.cfg)
793 # Display information
794 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
795 runner.cfg.VARS.application), 1)
797 # Get the default directory where to put the packages
798 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
800 src.ensure_path_exists(package_default_path)
802 elif package_type == PROJECT:
803 # check that the project is visible by SAT
804 if options.project not in runner.cfg.PROJECTS.project_file_paths:
805 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
808 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
809 "\nPlease add it in the %(site)s file." % {
810 "proj" : options.project, "site" : site_path})
811 logger.write(src.printcolors.printcError(msg), 1)
812 logger.write("\n", 1)
816 src.printcolors.print_value(logger, "Package type", package_type, 2)
818 # get the name of the archive or construct it
820 if os.path.basename(options.name) == options.name:
821 # only a name (not a path)
822 archive_name = options.name
823 dir_name = package_default_path
825 archive_name = os.path.basename(options.name)
826 dir_name = os.path.dirname(options.name)
829 if archive_name[-len(".tgz"):] == ".tgz":
830 archive_name = archive_name[:-len(".tgz")]
831 if archive_name[-len(".tar.gz"):] == ".tar.gz":
832 archive_name = archive_name[:-len(".tar.gz")]
835 dir_name = package_default_path
836 if package_type == BINARY:
837 archive_name = (runner.cfg.APPLICATION.name +
839 runner.cfg.VARS.dist)
841 if package_type == SOURCE:
842 archive_name = (runner.cfg.APPLICATION.name +
846 archive_name = (runner.cfg.APPLICATION.name +
852 if package_type == PROJECT:
853 project_name, __ = os.path.splitext(
854 os.path.basename(options.project))
855 archive_name = ("PROJECT" +
859 if package_type == SAT:
860 archive_name = ("salomeTools" +
862 runner.cfg.INTERNAL.sat_version)
864 path_targz = os.path.join(dir_name, archive_name + ".tgz")
866 # Print the path of the package
867 src.printcolors.print_value(logger, "Package path", path_targz, 2)
869 # Create a working directory for all files that are produced during the
870 # package creation and that will be removed at the end of the command
871 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
872 runner.cfg.VARS.datehour)
873 src.ensure_path_exists(tmp_working_dir)
874 logger.write("\n", 5)
875 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
877 logger.write("\n", 3)
879 msg = _("Preparation of files to add to the archive")
880 logger.write(src.printcolors.printcLabel(msg), 2)
881 logger.write("\n", 2)
883 if package_type == BINARY:
884 d_files_to_add = binary_package(runner.cfg,
888 if not(d_files_to_add):
891 # Create and add the source package
892 # if the option "with_sources" is called
893 if options.with_sources:
894 logger.write(_("Create a source archive (can be long) ... "), 3)
895 tmp_pkg_src_name = runner.cfg.APPLICATION.name + "-" + "SRC.tgz"
896 tmp_pkg_src_path = os.path.join(tmp_working_dir, tmp_pkg_src_name)
897 package_options = runner.cfg.VARS.application
898 package_options += " --sources --with_vcs --name "
899 package_options += tmp_pkg_src_path
900 # sat package <package_options>
901 runner.package(package_options,
904 logger_add_link = logger)
905 d_files_to_add["SOURCES PACKAGE"] = (tmp_pkg_src_path,
907 logger.write(src.printcolors.printc("OK"), 3)
908 logger.write("\n", 3)
910 if package_type == SOURCE:
911 d_files_to_add = source_package(runner,
917 if package_type == PROJECT:
918 d_files_to_add = project_package(options.project, tmp_working_dir)
920 if package_type == SAT:
921 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
923 # Add the README file in the package
924 local_readme_tmp_path = add_readme(runner.cfg,
927 d_files_to_add["README"] = (local_readme_tmp_path, "README")
929 logger.write("\n", 2)
931 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
932 logger.write("\n", 2)
935 # Creating the object tarfile
936 tar = tarfile.open(path_targz, mode='w:gz')
938 # Add the files to the tarfile object
939 res = add_files(tar, archive_name, d_files_to_add, logger)
941 except KeyboardInterrupt:
942 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
943 logger.write(_("Removing the temporary working directory ... "), 1)
944 # remove the working directory
945 shutil.rmtree(tmp_working_dir)
946 logger.write(_("OK"), 1)
947 logger.write(_("\n"), 1)
950 # remove the working directory
951 shutil.rmtree(tmp_working_dir)
953 # Print again the path of the package
954 logger.write("\n", 2)
955 src.printcolors.print_value(logger, "Package path", path_targz, 2)