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('s', 'sources', 'boolean', 'sources',
87 _('Optional: Produce a compilable archive of the sources of the '
88 'application.'), False)
89 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
90 _('Optional: Only source package: do not make archive of vcs products.'),
92 parser.add_option('p', 'project', 'string', 'project',
93 _('Optional: Produce an archive that contains a project.'), "")
94 parser.add_option('t', 'salometools', 'boolean', 'sat',
95 _('Optional: Produce an archive that contains salomeTools.'), False)
96 parser.add_option('n', 'name', 'string', 'name',
97 _('Optional: The name or full path of the archive.'), None)
99 def add_files(tar, name_archive, d_content, logger):
100 '''Create an archive containing all directories and files that are given in
101 the d_content argument.
103 :param tar tarfile: The tarfile instance used to make the archive.
104 :param name_archive str: The name of the archive to make.
105 :param d_content dict: The dictionary that contain all directories and files
106 to add in the archive.
108 (path_on_local_machine, path_in_archive)
109 :param logger Logger: the logging instance
110 :return: 0 if success, 1 if not.
113 # get the max length of the messages in order to make the display
114 max_len = len(max(d_content.keys(), key=len))
117 # loop over each directory or file stored in the d_content dictionary
118 for name in d_content.keys():
119 # display information
120 len_points = max_len - len(name)
121 logger.write(name + " " + len_points * "." + " ", 3)
122 # Get the local path and the path in archive
123 # of the directory or file to add
124 local_path, archive_path = d_content[name]
125 in_archive = os.path.join(name_archive, archive_path)
126 # Add it in the archive
128 tar.add(local_path, arcname=in_archive)
129 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
130 except Exception as e:
131 logger.write(src.printcolors.printcError(_("KO ")), 3)
132 logger.write(str(e), 3)
134 logger.write("\n", 3)
137 def produce_relative_launcher(config,
142 '''Create a specific SALOME launcher for the binary package. This launcher
145 :param config Config: The global configuration.
146 :param logger Logger: the logging instance
147 :param file_dir str: the directory where to put the launcher
148 :param file_name str: The launcher name
149 :param binaries_dir_name str: the name of the repository where the binaries
151 :return: the path of the produced launcher
155 # Get the launcher template
156 profile_install_dir = os.path.join(binaries_dir_name,
157 config.APPLICATION.profile.product)
158 withProfile = src.fileEnviron.withProfile
159 withProfile = withProfile.replace(
160 "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
161 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
162 withProfile = withProfile.replace(
163 "os.path.join( 'PROFILE_INSTALL_DIR'",
164 "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
166 before, after = withProfile.split(
167 "# here your local standalone environment\n")
169 # create an environment file writer
170 writer = src.environment.FileEnvWriter(config,
175 filepath = os.path.join(file_dir, file_name)
176 # open the file and write into it
177 launch_file = open(filepath, "w")
178 launch_file.write(before)
180 writer.write_cfgForPy_file(launch_file, for_package = binaries_dir_name)
181 launch_file.write(after)
184 # Little hack to put out_dir_Path outside the strings
185 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
187 # change the rights in order to make the file executable for everybody
199 def produce_relative_env_files(config,
203 '''Create some specific environment files for the binary package. These
204 files use relative paths.
206 :param config Config: The global configuration.
207 :param logger Logger: the logging instance
208 :param file_dir str: the directory where to put the files
209 :param binaries_dir_name str: the name of the repository where the binaries
211 :return: the list of path of the produced environment files
214 # create an environment file writer
215 writer = src.environment.FileEnvWriter(config,
221 filepath = writer.write_env_file("env_launch.sh",
224 for_package = binaries_dir_name)
226 # Little hack to put out_dir_Path as environment variable
227 src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
229 # change the rights in order to make the file executable for everybody
242 def binary_package(config, logger, options, tmp_working_dir):
243 '''Prepare a dictionary that stores all the needed directories and files to
244 add in a binary package.
246 :param config Config: The global configuration.
247 :param logger Logger: the logging instance
248 :param options OptResult: the options of the launched command
249 :param tmp_working_dir str: The temporary local directory containing some
250 specific directories or files needed in the
252 :return: the dictionary that stores all the needed directories and files to
253 add in a binary package.
254 {label : (path_on_local_machine, path_in_archive)}
258 # Get the list of product installation to add to the archive
259 l_products_name = config.APPLICATION.products.keys()
260 l_product_info = src.product.get_products_infos(l_products_name,
264 for prod_name, prod_info in l_product_info:
265 # ignore the native and fixed products
266 if (src.product.product_is_native(prod_info)
267 or src.product.product_is_fixed(prod_info)
268 or not src.product.product_compiles(prod_info)):
270 if src.product.check_installation(prod_info):
271 l_install_dir.append((prod_name, prod_info.install_dir))
273 l_not_installed.append(prod_name)
275 # Print warning or error if there are some missing products
276 if len(l_not_installed) > 0:
277 text_missing_prods = ""
278 for p_name in l_not_installed:
279 text_missing_prods += "-" + p_name + "\n"
280 if not options.force_creation:
281 msg = _("ERROR: there are missing products installations:")
282 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
287 msg = _("WARNING: there are missing products installations:")
288 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
292 # construct the name of the directory that will contain the binaries
293 binaries_dir_name = "BINARIES-" + config.VARS.dist
295 # construct the correlation table between the product names, there
296 # actual install directories and there install directory in archive
298 for prod_name, install_dir in l_install_dir:
299 path_in_archive = os.path.join(binaries_dir_name, prod_name)
300 d_products[prod_name] = (install_dir, path_in_archive)
302 # create the relative launcher and add it to the files to add
303 if "profile" in config.APPLICATION:
304 launcher_name = config.APPLICATION.profile.launcher_name
305 launcher_package = produce_relative_launcher(config,
311 d_products["launcher"] = (launcher_package, launcher_name)
313 # No profile, it means that there has to be some environment files
314 env_file = produce_relative_env_files(config,
319 d_products["environment file"] = (env_file, "env_launch.sh")
323 def source_package(sat, config, logger, options, tmp_working_dir):
324 '''Prepare a dictionary that stores all the needed directories and files to
325 add in a source package.
327 :param config Config: The global configuration.
328 :param logger Logger: the logging instance
329 :param options OptResult: the options of the launched command
330 :param tmp_working_dir str: The temporary local directory containing some
331 specific directories or files needed in the
333 :return: the dictionary that stores all the needed directories and files to
334 add in a source package.
335 {label : (path_on_local_machine, path_in_archive)}
339 # Get all the products that are prepared using an archive
340 logger.write("Find archive products ... ")
341 d_archives, l_pinfo_vcs = get_archives(config, logger)
342 logger.write("Done\n")
344 if not options.with_vcs and len(l_pinfo_vcs) > 0:
345 # Make archives with the products that are not prepared using an archive
346 # (git, cvs, svn, etc)
347 logger.write("Construct archives for vcs products ... ")
348 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
353 logger.write("Done\n")
356 logger.write("Create the project ... ")
357 d_project = create_project_for_src_package(config,
360 logger.write("Done\n")
363 tmp_sat = add_salomeTools(config, tmp_working_dir)
364 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
366 # Add a sat symbolic link if not win
367 if not src.architecture.is_windows():
368 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
372 # In the jobs, os.getcwd() can fail
373 t = config.USER.workdir
374 os.chdir(tmp_working_dir)
375 if os.path.lexists(tmp_satlink_path):
376 os.remove(tmp_satlink_path)
377 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
380 d_sat["sat link"] = (tmp_satlink_path, "sat")
382 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
384 def get_archives(config, logger):
385 '''Find all the products that are get using an archive and all the products
386 that are get using a vcs (git, cvs, svn) repository.
388 :param config Config: The global configuration.
389 :param logger Logger: the logging instance
390 :return: the dictionary {name_product :
391 (local path of its archive, path in the package of its archive )}
392 and the list of specific configuration corresponding to the vcs
396 # Get the list of product informations
397 l_products_name = config.APPLICATION.products.keys()
398 l_product_info = src.product.get_products_infos(l_products_name,
402 for p_name, p_info in l_product_info:
403 # ignore the native and fixed products
404 if (src.product.product_is_native(p_info)
405 or src.product.product_is_fixed(p_info)):
407 if p_info.get_source == "archive":
408 archive_path = p_info.archive_info.archive_name
409 archive_name = os.path.basename(archive_path)
411 l_pinfo_vcs.append((p_name, p_info))
413 d_archives[p_name] = (archive_path,
414 os.path.join(ARCHIVE_DIR, archive_name))
415 return d_archives, l_pinfo_vcs
417 def add_salomeTools(config, tmp_working_dir):
418 '''Prepare a version of salomeTools that has a specific site.pyconf file
419 configured for a source package.
421 :param config Config: The global configuration.
422 :param tmp_working_dir str: The temporary local directory containing some
423 specific directories or files needed in the
425 :return: The path to the local salomeTools directory to add in the package
428 # Copy sat in the temporary working directory
429 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
430 sat_running_path = src.Path(config.VARS.salometoolsway)
431 sat_running_path.copy(sat_tmp_path)
433 # Update the site.pyconf file that contains the path to the project
434 site_pyconf_name = "site.pyconf"
435 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
436 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
437 ff = open(site_pyconf_file, "w")
438 ff.write(SITE_TEMPLATE)
441 return sat_tmp_path.path
443 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
444 '''For sources package that require that all products are get using an
445 archive, one has to create some archive for the vcs products.
446 So this method calls the clean and source command of sat and then create
449 :param l_pinfo_vcs List: The list of specific configuration corresponding to
451 :param sat Sat: The Sat instance that can be called to clean and source the
453 :param config Config: The global configuration.
454 :param logger Logger: the logging instance
455 :param tmp_working_dir str: The temporary local directory containing some
456 specific directories or files needed in the
458 :return: the dictionary that stores all the archives to add in the source
459 package. {label : (path_on_local_machine, path_in_archive)}
462 # clean the source directory of all the vcs products, then use the source
463 # command and thus construct an archive that will not contain the patches
464 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
466 logger.write(_("clean sources\n"))
467 args_clean = config.VARS.application
468 args_clean += " --sources --products "
469 args_clean += ",".join(l_prod_names)
470 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
472 logger.write(_("get sources"))
473 args_source = config.VARS.application
474 args_source += " --products "
475 args_source += ",".join(l_prod_names)
476 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
478 # make the new archives
480 for pn, pinfo in l_pinfo_vcs:
481 path_archive = make_archive(pn, pinfo, tmp_working_dir)
482 d_archives_vcs[pn] = (path_archive,
483 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
484 return d_archives_vcs
486 def make_archive(prod_name, prod_info, where):
487 '''Create an archive of a product by searching its source directory.
489 :param prod_name str: The name of the product.
490 :param prod_info Config: The specific configuration corresponding to the
492 :param where str: The path of the repository where to put the resulting
494 :return: The path of the resulting archive
497 path_targz_prod = os.path.join(where, prod_name + ".tgz")
498 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
499 local_path = prod_info.source_dir
500 tar_prod.add(local_path, arcname=prod_name)
502 return path_targz_prod
504 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
505 '''Create a specific project for a source package.
507 :param config Config: The global configuration.
508 :param tmp_working_dir str: The temporary local directory containing some
509 specific directories or files needed in the
511 :param with_vcs boolean: True if the package is with vcs products (not
512 transformed into archive products)
513 :return: The dictionary
514 {"project" : (produced project, project path in the archive)}
518 # Create in the working temporary directory the full project tree
519 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
520 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
522 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
525 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
528 patches_tmp_dir = os.path.join(project_tmp_dir,
531 application_tmp_dir = os.path.join(project_tmp_dir,
533 for directory in [project_tmp_dir,
534 compil_scripts_tmp_dir,
537 application_tmp_dir]:
538 src.ensure_path_exists(directory)
540 # Create the pyconf that contains the information of the project
541 project_pyconf_name = "project.pyconf"
542 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
543 ff = open(project_pyconf_file, "w")
544 ff.write(PROJECT_TEMPLATE)
547 # Loop over the products to get there pyconf and all the scripts
548 # (compilation, environment, patches)
549 # and create the pyconf file to add to the project
550 lproducts_name = config.APPLICATION.products.keys()
551 l_products = src.product.get_products_infos(lproducts_name, config)
552 for p_name, p_info in l_products:
553 # ignore native and fixed products
554 if (src.product.product_is_native(p_info) or
555 src.product.product_is_fixed(p_info)):
557 find_product_scripts_and_pyconf(p_name,
561 compil_scripts_tmp_dir,
564 products_pyconf_tmp_dir)
566 find_application_pyconf(config, application_tmp_dir)
568 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
571 def find_product_scripts_and_pyconf(p_name,
575 compil_scripts_tmp_dir,
578 products_pyconf_tmp_dir):
579 '''Create a specific pyconf file for a given product. Get its environment
580 script, its compilation script and patches and put it in the temporary
581 working directory. This method is used in the source package in order to
582 construct the specific project.
584 :param p_name str: The name of the product.
585 :param p_info Config: The specific configuration corresponding to the
587 :param config Config: The global configuration.
588 :param with_vcs boolean: True if the package is with vcs products (not
589 transformed into archive products)
590 :param compil_scripts_tmp_dir str: The path to the temporary compilation
591 scripts directory of the project.
592 :param env_scripts_tmp_dir str: The path to the temporary environment script
593 directory of the project.
594 :param patches_tmp_dir str: The path to the temporary patch scripts
595 directory of the project.
596 :param products_pyconf_tmp_dir str: The path to the temporary product
597 scripts directory of the project.
600 # read the pyconf of the product
601 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
602 config.PATHS.PRODUCTPATH)
603 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
605 # find the compilation script if any
606 if src.product.product_has_script(p_info):
607 compil_script_path = src.Path(p_info.compil_script)
608 compil_script_path.copy(compil_scripts_tmp_dir)
609 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
610 p_info.compil_script)
611 # find the environment script if any
612 if src.product.product_has_env_script(p_info):
613 env_script_path = src.Path(p_info.environ.env_script)
614 env_script_path.copy(env_scripts_tmp_dir)
615 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
616 p_info.environ.env_script)
617 # find the patches if any
618 if src.product.product_has_patches(p_info):
619 patches = src.pyconf.Sequence()
620 for patch_path in p_info.patches:
621 p_path = src.Path(patch_path)
622 p_path.copy(patches_tmp_dir)
623 patches.append(os.path.basename(patch_path), "")
625 product_pyconf_cfg[p_info.section].patches = patches
628 # put in the pyconf file the resolved values
629 for info in ["git_info", "cvs_info", "svn_info"]:
631 for key in p_info[info]:
632 product_pyconf_cfg[p_info.section][info][key] = p_info[
635 # if the product is not archive, then make it become archive.
636 if src.product.product_is_vcs(p_info):
637 product_pyconf_cfg[p_info.section].get_source = "archive"
638 if not "archive_info" in product_pyconf_cfg[p_info.section]:
639 product_pyconf_cfg[p_info.section].addMapping("archive_info",
640 src.pyconf.Mapping(product_pyconf_cfg),
642 product_pyconf_cfg[p_info.section
643 ].archive_info.archive_name = p_info.name + ".tgz"
645 # write the pyconf file to the temporary project location
646 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
648 ff = open(product_tmp_pyconf_path, 'w')
649 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
650 product_pyconf_cfg.__save__(ff, 1)
653 def find_application_pyconf(config, application_tmp_dir):
654 '''Find the application pyconf file and put it in the specific temporary
655 directory containing the specific project of a source package.
657 :param config Config: The global configuration.
658 :param application_tmp_dir str: The path to the temporary application
659 scripts directory of the project.
661 # read the pyconf of the application
662 application_name = config.VARS.application
663 application_pyconf_path = src.find_file_in_lpath(
664 application_name + ".pyconf",
665 config.PATHS.APPLICATIONPATH)
666 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
669 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
670 application_pyconf_cfg,
672 'VARS.salometoolsway + $VARS.sep + ".."')
674 # Prevent from compilation in base
675 application_pyconf_cfg.APPLICATION.no_base = "yes"
677 # write the pyconf file to the temporary application location
678 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
679 application_name + ".pyconf")
680 ff = open(application_tmp_pyconf_path, 'w')
681 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
682 application_pyconf_cfg.__save__(ff, 1)
685 def project_package(project_file_path, tmp_working_dir):
686 '''Prepare a dictionary that stores all the needed directories and files to
687 add in a project package.
689 :param project_file_path str: The path to the local project.
690 :param tmp_working_dir str: The temporary local directory containing some
691 specific directories or files needed in the
693 :return: the dictionary that stores all the needed directories and files to
694 add in a project package.
695 {label : (path_on_local_machine, path_in_archive)}
699 # Read the project file and get the directories to add to the package
700 project_pyconf_cfg = src.pyconf.Config(project_file_path)
701 paths = {"ARCHIVEPATH" : "archives",
702 "APPLICATIONPATH" : "applications",
703 "PRODUCTPATH" : "products",
705 "MACHINEPATH" : "machines"}
706 # Loop over the project paths and add it
708 if path not in project_pyconf_cfg:
710 # Add the directory to the files to add in the package
711 d_project[path] = (project_pyconf_cfg[path], paths[path])
712 # Modify the value of the path in the package
713 project_pyconf_cfg[path] = src.pyconf.Reference(
716 'project_path + "/' + paths[path] + '"')
719 if "project_path" not in project_pyconf_cfg:
720 project_pyconf_cfg.addMapping("project_path",
721 src.pyconf.Mapping(project_pyconf_cfg),
723 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
727 # Write the project pyconf file
728 project_file_name = os.path.basename(project_file_path)
729 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
730 ff = open(project_pyconf_tmp_path, 'w')
731 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
732 project_pyconf_cfg.__save__(ff, 1)
734 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
738 def add_readme(config, package_type, where):
739 readme_path = os.path.join(where, "README")
740 f = open(readme_path, 'w')
741 # prepare substitution dictionary
743 if package_type == BINARY:
744 d['application'] = config.VARS.application
745 d['user'] = config.VARS.user
746 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
747 d['version'] = config.INTERNAL.sat_version
748 d['dist'] = config.VARS.dist
749 if 'profile' in config.APPLICATION:
750 d['launcher'] = config.APPLICATION.profile.launcher_name
751 readme_template_path = os.path.join(config.VARS.internal_dir,
752 "README_BIN.template")
753 if package_type == SOURCE:
754 d['application'] = config.VARS.application
755 d['user'] = config.VARS.user
756 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
757 d['version'] = config.INTERNAL.sat_version
758 if 'profile' in config.APPLICATION:
759 d['profile'] = config.APPLICATION.profile.product
760 d['launcher'] = config.APPLICATION.profile.launcher_name
761 readme_template_path = os.path.join(config.VARS.internal_dir,
762 "README_SRC.template")
764 if package_type == PROJECT:
765 d['user'] = config.VARS.user
766 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
767 d['version'] = config.INTERNAL.sat_version
768 readme_template_path = os.path.join(config.VARS.internal_dir,
769 "README_PROJECT.template")
771 if package_type == SAT:
772 d['user'] = config.VARS.user
773 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
774 d['version'] = config.INTERNAL.sat_version
775 readme_template_path = os.path.join(config.VARS.internal_dir,
776 "README_SAT.template")
778 f.write(src.template.substitute(readme_template_path, d))
784 '''method that is called when salomeTools is called with --help option.
786 :return: The text to display for the package command description.
789 return _("The package command creates an archive.\nThere are 4 kinds of "
790 "archive:\n 1- The binary archive. It contains all the product "
791 "installation directories and a launcher,\n 2- The sources archive."
792 " It contains the products archives, a project corresponding to "
793 "the application and salomeTools,\n 3- The project archive. It "
794 "contains a project (give the project file path as argument),\n 4-"
795 " The salomeTools archive. It contains salomeTools.\n\nexample:"
796 "\nsat package SALOME-master --sources")
798 def run(args, runner, logger):
799 '''method that is called when salomeTools is called with package parameter.
803 (options, args) = parser.parse_args(args)
805 # Check that a type of package is called, and only one
806 all_option_types = (options.binaries,
808 options.project not in ["", None],
811 # Check if no option for package type
812 if all_option_types.count(True) == 0:
813 msg = _("Error: Precise a type for the package\nUse one of the "
814 "following options: --binaries, --sources, --project or"
816 logger.write(src.printcolors.printcError(msg), 1)
817 logger.write("\n", 1)
820 # Check for only one option for package type
821 if all_option_types.count(True) > 1:
822 msg = _("Error: You can use only one type for the package\nUse only one"
823 " of the following options: --binaries, --sources, --project or"
825 logger.write(src.printcolors.printcError(msg), 1)
826 logger.write("\n", 1)
829 # Get the package type
831 package_type = BINARY
833 package_type = SOURCE
835 package_type = PROJECT
839 # The repository where to put the package if not Binary or Source
840 package_default_path = runner.cfg.USER.workdir
842 if package_type in [BINARY, SOURCE]:
843 # Check that the command has been called with an application
844 src.check_config_has_application(runner.cfg)
846 # Display information
847 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
848 runner.cfg.VARS.application), 1)
850 # Get the default directory where to put the packages
851 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
853 src.ensure_path_exists(package_default_path)
855 elif package_type == PROJECT:
856 # check that the project is visible by SAT
857 if options.project not in runner.cfg.PROJECTS.project_file_paths:
858 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
861 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
862 "\nPlease add it in the %(site)s file." % {
863 "proj" : options.project, "site" : site_path})
864 logger.write(src.printcolors.printcError(msg), 1)
865 logger.write("\n", 1)
869 src.printcolors.print_value(logger, "Package type", package_type, 2)
871 # get the name of the archive or construct it
873 if os.path.basename(options.name) == options.name:
874 # only a name (not a path)
875 archive_name = options.name
876 dir_name = package_default_path
878 archive_name = os.path.basename(options.name)
879 dir_name = os.path.dirname(options.name)
882 if archive_name[-len(".tgz"):] == ".tgz":
883 archive_name = archive_name[:-len(".tgz")]
884 if archive_name[-len(".tar.gz"):] == ".tar.gz":
885 archive_name = archive_name[:-len(".tar.gz")]
888 dir_name = package_default_path
889 if package_type == BINARY:
890 archive_name = (runner.cfg.APPLICATION.name +
892 runner.cfg.VARS.dist)
894 if package_type == SOURCE:
895 archive_name = (runner.cfg.APPLICATION.name +
899 archive_name = (runner.cfg.APPLICATION.name +
905 if package_type == PROJECT:
906 project_name, __ = os.path.splitext(
907 os.path.basename(options.project))
908 archive_name = ("PROJECT" +
912 if package_type == SAT:
913 archive_name = ("salomeTools" +
915 runner.cfg.INTERNAL.sat_version)
917 path_targz = os.path.join(dir_name, archive_name + ".tgz")
919 # Print the path of the package
920 src.printcolors.print_value(logger, "Package path", path_targz, 2)
922 # Create a working directory for all files that are produced during the
923 # package creation and that will be removed at the end of the command
924 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
925 runner.cfg.VARS.datehour)
926 src.ensure_path_exists(tmp_working_dir)
927 logger.write("\n", 5)
928 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
930 logger.write("\n", 3)
932 msg = _("Preparation of files to add to the archive")
933 logger.write(src.printcolors.printcLabel(msg), 2)
934 logger.write("\n", 2)
936 if package_type == BINARY:
937 d_files_to_add = binary_package(runner.cfg,
941 if not(d_files_to_add):
944 if package_type == SOURCE:
945 d_files_to_add = source_package(runner,
951 if package_type == PROJECT:
952 d_files_to_add = project_package(options.project, tmp_working_dir)
954 if package_type == SAT:
955 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
957 # Add the README file in the package
958 local_readme_tmp_path = add_readme(runner.cfg,
961 d_files_to_add["README"] = (local_readme_tmp_path, "README")
963 logger.write("\n", 2)
965 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
966 logger.write("\n", 2)
969 # Creating the object tarfile
970 tar = tarfile.open(path_targz, mode='w:gz')
972 # Add the files to the tarfile object
973 res = add_files(tar, archive_name, d_files_to_add, logger)
975 except KeyboardInterrupt:
976 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
977 logger.write(_("Removing the temporary working directory ... "), 1)
978 # remove the working directory
979 shutil.rmtree(tmp_working_dir)
980 logger.write(_("OK"), 1)
981 logger.write(_("\n"), 1)
984 # remove the working directory
985 shutil.rmtree(tmp_working_dir)
987 # Print again the path of the package
988 logger.write("\n", 2)
989 src.printcolors.print_value(logger, "Package path", path_targz, 2)