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)
98 parser.add_option('', 'add_files', 'list2', 'add_files',
99 _('Optional: The list of additional files to add to the archive.'), [])
100 parser.add_option('', 'without_commercial', 'boolean', 'with_commercial',
101 _('Optional: do not add commercial licence.'), False)
103 def add_files(tar, name_archive, d_content, logger):
104 '''Create an archive containing all directories and files that are given in
105 the d_content argument.
107 :param tar tarfile: The tarfile instance used to make the archive.
108 :param name_archive str: The name of the archive to make.
109 :param d_content dict: The dictionary that contain all directories and files
110 to add in the archive.
112 (path_on_local_machine, path_in_archive)
113 :param logger Logger: the logging instance
114 :return: 0 if success, 1 if not.
117 # get the max length of the messages in order to make the display
118 max_len = len(max(d_content.keys(), key=len))
121 # loop over each directory or file stored in the d_content dictionary
122 for name in d_content.keys():
123 # display information
124 len_points = max_len - len(name)
125 logger.write(name + " " + len_points * "." + " ", 3)
126 # Get the local path and the path in archive
127 # of the directory or file to add
128 local_path, archive_path = d_content[name]
129 in_archive = os.path.join(name_archive, archive_path)
130 # Add it in the archive
132 tar.add(local_path, arcname=in_archive)
133 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
134 except Exception as e:
135 logger.write(src.printcolors.printcError(_("KO ")), 3)
136 logger.write(str(e), 3)
138 logger.write("\n", 3)
141 def produce_relative_launcher(config,
146 with_commercial=True):
147 '''Create a specific SALOME launcher for the binary package. This launcher
150 :param config Config: The global configuration.
151 :param logger Logger: the logging instance
152 :param file_dir str: the directory where to put the launcher
153 :param file_name str: The launcher name
154 :param binaries_dir_name str: the name of the repository where the binaries
156 :return: the path of the produced launcher
160 # Get the launcher template
161 profile_install_dir = os.path.join(binaries_dir_name,
162 config.APPLICATION.profile.product)
163 withProfile = src.fileEnviron.withProfile
164 withProfile = withProfile.replace(
165 "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
166 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
167 withProfile = withProfile.replace(
168 "os.path.join( 'PROFILE_INSTALL_DIR'",
169 "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
171 before, after = withProfile.split(
172 "# here your local standalone environment\n")
174 # create an environment file writer
175 writer = src.environment.FileEnvWriter(config,
180 filepath = os.path.join(file_dir, file_name)
181 # open the file and write into it
182 launch_file = open(filepath, "w")
183 launch_file.write(before)
185 writer.write_cfgForPy_file(launch_file,
186 for_package = binaries_dir_name,
187 with_commercial=with_commercial)
188 launch_file.write(after)
191 # Little hack to put out_dir_Path outside the strings
192 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
194 # change the rights in order to make the file executable for everybody
206 def produce_relative_env_files(config,
210 '''Create some specific environment files for the binary package. These
211 files use relative paths.
213 :param config Config: The global configuration.
214 :param logger Logger: the logging instance
215 :param file_dir str: the directory where to put the files
216 :param binaries_dir_name str: the name of the repository where the binaries
218 :return: the list of path of the produced environment files
221 # create an environment file writer
222 writer = src.environment.FileEnvWriter(config,
228 filepath = writer.write_env_file("env_launch.sh",
231 for_package = binaries_dir_name)
233 # Little hack to put out_dir_Path as environment variable
234 src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
236 # change the rights in order to make the file executable for everybody
249 def binary_package(config, logger, options, tmp_working_dir):
250 '''Prepare a dictionary that stores all the needed directories and files to
251 add in a binary package.
253 :param config Config: The global configuration.
254 :param logger Logger: the logging instance
255 :param options OptResult: the options of the launched command
256 :param tmp_working_dir str: The temporary local directory containing some
257 specific directories or files needed in the
259 :return: the dictionary that stores all the needed directories and files to
260 add in a binary package.
261 {label : (path_on_local_machine, path_in_archive)}
265 # Get the list of product installation to add to the archive
266 l_products_name = config.APPLICATION.products.keys()
267 l_product_info = src.product.get_products_infos(l_products_name,
271 for prod_name, prod_info in l_product_info:
272 # ignore the native and fixed products
273 if (src.product.product_is_native(prod_info)
274 or src.product.product_is_fixed(prod_info)
275 or not src.product.product_compiles(prod_info)):
277 if src.product.check_installation(prod_info):
278 l_install_dir.append((prod_name, prod_info.install_dir))
280 l_not_installed.append(prod_name)
282 # Print warning or error if there are some missing products
283 if len(l_not_installed) > 0:
284 text_missing_prods = ""
285 for p_name in l_not_installed:
286 text_missing_prods += "-" + p_name + "\n"
287 if not options.force_creation:
288 msg = _("ERROR: there are missing products installations:")
289 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
294 msg = _("WARNING: there are missing products installations:")
295 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
299 # construct the name of the directory that will contain the binaries
300 binaries_dir_name = "BINARIES-" + config.VARS.dist
302 # construct the correlation table between the product names, there
303 # actual install directories and there install directory in archive
305 for prod_name, install_dir in l_install_dir:
306 path_in_archive = os.path.join(binaries_dir_name, prod_name)
307 d_products[prod_name] = (install_dir, path_in_archive)
309 # create the relative launcher and add it to the files to add
310 if "profile" in config.APPLICATION:
311 launcher_name = config.APPLICATION.profile.launcher_name
312 launcher_package = produce_relative_launcher(config,
317 not(options.without_commercial))
319 d_products["launcher"] = (launcher_package, launcher_name)
321 # No profile, it means that there has to be some environment files
322 env_file = produce_relative_env_files(config,
327 d_products["environment file"] = (env_file, "env_launch.sh")
331 def source_package(sat, config, logger, options, tmp_working_dir):
332 '''Prepare a dictionary that stores all the needed directories and files to
333 add in a source package.
335 :param config Config: The global configuration.
336 :param logger Logger: the logging instance
337 :param options OptResult: the options of the launched command
338 :param tmp_working_dir str: The temporary local directory containing some
339 specific directories or files needed in the
341 :return: the dictionary that stores all the needed directories and files to
342 add in a source package.
343 {label : (path_on_local_machine, path_in_archive)}
347 # Get all the products that are prepared using an archive
348 logger.write("Find archive products ... ")
349 d_archives, l_pinfo_vcs = get_archives(config, logger)
350 logger.write("Done\n")
352 if not options.with_vcs and len(l_pinfo_vcs) > 0:
353 # Make archives with the products that are not prepared using an archive
354 # (git, cvs, svn, etc)
355 logger.write("Construct archives for vcs products ... ")
356 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
361 logger.write("Done\n")
364 logger.write("Create the project ... ")
365 d_project = create_project_for_src_package(config,
368 logger.write("Done\n")
371 tmp_sat = add_salomeTools(config, tmp_working_dir)
372 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
374 # Add a sat symbolic link if not win
375 if not src.architecture.is_windows():
376 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
380 # In the jobs, os.getcwd() can fail
381 t = config.USER.workdir
382 os.chdir(tmp_working_dir)
383 if os.path.lexists(tmp_satlink_path):
384 os.remove(tmp_satlink_path)
385 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
388 d_sat["sat link"] = (tmp_satlink_path, "sat")
390 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
392 def get_archives(config, logger):
393 '''Find all the products that are get using an archive and all the products
394 that are get using a vcs (git, cvs, svn) repository.
396 :param config Config: The global configuration.
397 :param logger Logger: the logging instance
398 :return: the dictionary {name_product :
399 (local path of its archive, path in the package of its archive )}
400 and the list of specific configuration corresponding to the vcs
404 # Get the list of product informations
405 l_products_name = config.APPLICATION.products.keys()
406 l_product_info = src.product.get_products_infos(l_products_name,
410 for p_name, p_info in l_product_info:
411 # ignore the native and fixed products
412 if (src.product.product_is_native(p_info)
413 or src.product.product_is_fixed(p_info)):
415 if p_info.get_source == "archive":
416 archive_path = p_info.archive_info.archive_name
417 archive_name = os.path.basename(archive_path)
419 l_pinfo_vcs.append((p_name, p_info))
421 d_archives[p_name] = (archive_path,
422 os.path.join(ARCHIVE_DIR, archive_name))
423 return d_archives, l_pinfo_vcs
425 def add_salomeTools(config, tmp_working_dir):
426 '''Prepare a version of salomeTools that has a specific site.pyconf file
427 configured for a source package.
429 :param config Config: The global configuration.
430 :param tmp_working_dir str: The temporary local directory containing some
431 specific directories or files needed in the
433 :return: The path to the local salomeTools directory to add in the package
436 # Copy sat in the temporary working directory
437 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
438 sat_running_path = src.Path(config.VARS.salometoolsway)
439 sat_running_path.copy(sat_tmp_path)
441 # Update the site.pyconf file that contains the path to the project
442 site_pyconf_name = "site.pyconf"
443 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
444 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
445 ff = open(site_pyconf_file, "w")
446 ff.write(SITE_TEMPLATE)
449 return sat_tmp_path.path
451 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
452 '''For sources package that require that all products are get using an
453 archive, one has to create some archive for the vcs products.
454 So this method calls the clean and source command of sat and then create
457 :param l_pinfo_vcs List: The list of specific configuration corresponding to
459 :param sat Sat: The Sat instance that can be called to clean and source the
461 :param config Config: The global configuration.
462 :param logger Logger: the logging instance
463 :param tmp_working_dir str: The temporary local directory containing some
464 specific directories or files needed in the
466 :return: the dictionary that stores all the archives to add in the source
467 package. {label : (path_on_local_machine, path_in_archive)}
470 # clean the source directory of all the vcs products, then use the source
471 # command and thus construct an archive that will not contain the patches
472 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
474 logger.write(_("clean sources\n"))
475 args_clean = config.VARS.application
476 args_clean += " --sources --products "
477 args_clean += ",".join(l_prod_names)
478 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
480 logger.write(_("get sources"))
481 args_source = config.VARS.application
482 args_source += " --products "
483 args_source += ",".join(l_prod_names)
484 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
486 # make the new archives
488 for pn, pinfo in l_pinfo_vcs:
489 path_archive = make_archive(pn, pinfo, tmp_working_dir)
490 d_archives_vcs[pn] = (path_archive,
491 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
492 return d_archives_vcs
494 def make_archive(prod_name, prod_info, where):
495 '''Create an archive of a product by searching its source directory.
497 :param prod_name str: The name of the product.
498 :param prod_info Config: The specific configuration corresponding to the
500 :param where str: The path of the repository where to put the resulting
502 :return: The path of the resulting archive
505 path_targz_prod = os.path.join(where, prod_name + ".tgz")
506 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
507 local_path = prod_info.source_dir
508 tar_prod.add(local_path, arcname=prod_name)
510 return path_targz_prod
512 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
513 '''Create a specific project for a source package.
515 :param config Config: The global configuration.
516 :param tmp_working_dir str: The temporary local directory containing some
517 specific directories or files needed in the
519 :param with_vcs boolean: True if the package is with vcs products (not
520 transformed into archive products)
521 :return: The dictionary
522 {"project" : (produced project, project path in the archive)}
526 # Create in the working temporary directory the full project tree
527 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
528 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
530 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
533 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
536 patches_tmp_dir = os.path.join(project_tmp_dir,
539 application_tmp_dir = os.path.join(project_tmp_dir,
541 for directory in [project_tmp_dir,
542 compil_scripts_tmp_dir,
545 application_tmp_dir]:
546 src.ensure_path_exists(directory)
548 # Create the pyconf that contains the information of the project
549 project_pyconf_name = "project.pyconf"
550 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
551 ff = open(project_pyconf_file, "w")
552 ff.write(PROJECT_TEMPLATE)
555 # Loop over the products to get there pyconf and all the scripts
556 # (compilation, environment, patches)
557 # and create the pyconf file to add to the project
558 lproducts_name = config.APPLICATION.products.keys()
559 l_products = src.product.get_products_infos(lproducts_name, config)
560 for p_name, p_info in l_products:
561 # ignore native and fixed products
562 if (src.product.product_is_native(p_info) or
563 src.product.product_is_fixed(p_info)):
565 find_product_scripts_and_pyconf(p_name,
569 compil_scripts_tmp_dir,
572 products_pyconf_tmp_dir)
574 find_application_pyconf(config, application_tmp_dir)
576 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
579 def find_product_scripts_and_pyconf(p_name,
583 compil_scripts_tmp_dir,
586 products_pyconf_tmp_dir):
587 '''Create a specific pyconf file for a given product. Get its environment
588 script, its compilation script and patches and put it in the temporary
589 working directory. This method is used in the source package in order to
590 construct the specific project.
592 :param p_name str: The name of the product.
593 :param p_info Config: The specific configuration corresponding to the
595 :param config Config: The global configuration.
596 :param with_vcs boolean: True if the package is with vcs products (not
597 transformed into archive products)
598 :param compil_scripts_tmp_dir str: The path to the temporary compilation
599 scripts directory of the project.
600 :param env_scripts_tmp_dir str: The path to the temporary environment script
601 directory of the project.
602 :param patches_tmp_dir str: The path to the temporary patch scripts
603 directory of the project.
604 :param products_pyconf_tmp_dir str: The path to the temporary product
605 scripts directory of the project.
608 # read the pyconf of the product
609 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
610 config.PATHS.PRODUCTPATH)
611 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
613 # find the compilation script if any
614 if src.product.product_has_script(p_info):
615 compil_script_path = src.Path(p_info.compil_script)
616 compil_script_path.copy(compil_scripts_tmp_dir)
617 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
618 p_info.compil_script)
619 # find the environment script if any
620 if src.product.product_has_env_script(p_info):
621 env_script_path = src.Path(p_info.environ.env_script)
622 env_script_path.copy(env_scripts_tmp_dir)
623 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
624 p_info.environ.env_script)
625 # find the patches if any
626 if src.product.product_has_patches(p_info):
627 patches = src.pyconf.Sequence()
628 for patch_path in p_info.patches:
629 p_path = src.Path(patch_path)
630 p_path.copy(patches_tmp_dir)
631 patches.append(os.path.basename(patch_path), "")
633 product_pyconf_cfg[p_info.section].patches = patches
636 # put in the pyconf file the resolved values
637 for info in ["git_info", "cvs_info", "svn_info"]:
639 for key in p_info[info]:
640 product_pyconf_cfg[p_info.section][info][key] = p_info[
643 # if the product is not archive, then make it become archive.
644 if src.product.product_is_vcs(p_info):
645 product_pyconf_cfg[p_info.section].get_source = "archive"
646 if not "archive_info" in product_pyconf_cfg[p_info.section]:
647 product_pyconf_cfg[p_info.section].addMapping("archive_info",
648 src.pyconf.Mapping(product_pyconf_cfg),
650 product_pyconf_cfg[p_info.section
651 ].archive_info.archive_name = p_info.name + ".tgz"
653 # write the pyconf file to the temporary project location
654 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
656 ff = open(product_tmp_pyconf_path, 'w')
657 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
658 product_pyconf_cfg.__save__(ff, 1)
661 def find_application_pyconf(config, application_tmp_dir):
662 '''Find the application pyconf file and put it in the specific temporary
663 directory containing the specific project of a source package.
665 :param config Config: The global configuration.
666 :param application_tmp_dir str: The path to the temporary application
667 scripts directory of the project.
669 # read the pyconf of the application
670 application_name = config.VARS.application
671 application_pyconf_path = src.find_file_in_lpath(
672 application_name + ".pyconf",
673 config.PATHS.APPLICATIONPATH)
674 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
677 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
678 application_pyconf_cfg,
680 'VARS.salometoolsway + $VARS.sep + ".."')
682 # Prevent from compilation in base
683 application_pyconf_cfg.APPLICATION.no_base = "yes"
685 # write the pyconf file to the temporary application location
686 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
687 application_name + ".pyconf")
688 ff = open(application_tmp_pyconf_path, 'w')
689 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
690 application_pyconf_cfg.__save__(ff, 1)
693 def project_package(project_file_path, tmp_working_dir):
694 '''Prepare a dictionary that stores all the needed directories and files to
695 add in a project package.
697 :param project_file_path str: The path to the local project.
698 :param tmp_working_dir str: The temporary local directory containing some
699 specific directories or files needed in the
701 :return: the dictionary that stores all the needed directories and files to
702 add in a project package.
703 {label : (path_on_local_machine, path_in_archive)}
707 # Read the project file and get the directories to add to the package
708 project_pyconf_cfg = src.pyconf.Config(project_file_path)
709 paths = {"ARCHIVEPATH" : "archives",
710 "APPLICATIONPATH" : "applications",
711 "PRODUCTPATH" : "products",
713 "MACHINEPATH" : "machines"}
714 # Loop over the project paths and add it
716 if path not in project_pyconf_cfg:
718 # Add the directory to the files to add in the package
719 d_project[path] = (project_pyconf_cfg[path], paths[path])
720 # Modify the value of the path in the package
721 project_pyconf_cfg[path] = src.pyconf.Reference(
724 'project_path + "/' + paths[path] + '"')
727 if "project_path" not in project_pyconf_cfg:
728 project_pyconf_cfg.addMapping("project_path",
729 src.pyconf.Mapping(project_pyconf_cfg),
731 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
735 # Write the project pyconf file
736 project_file_name = os.path.basename(project_file_path)
737 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
738 ff = open(project_pyconf_tmp_path, 'w')
739 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
740 project_pyconf_cfg.__save__(ff, 1)
742 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
746 def add_readme(config, package_type, where):
747 readme_path = os.path.join(where, "README")
748 f = open(readme_path, 'w')
749 # prepare substitution dictionary
751 if package_type == BINARY:
752 d['application'] = config.VARS.application
753 d['user'] = config.VARS.user
754 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
755 d['version'] = config.INTERNAL.sat_version
756 d['dist'] = config.VARS.dist
757 if 'profile' in config.APPLICATION:
758 d['launcher'] = config.APPLICATION.profile.launcher_name
759 readme_template_path = os.path.join(config.VARS.internal_dir,
760 "README_BIN.template")
762 d['env_file'] = 'env_launch.sh'
763 readme_template_path = os.path.join(config.VARS.internal_dir,
764 "README_BIN_NO_PROFILE.template")
766 if package_type == SOURCE:
767 d['application'] = config.VARS.application
768 d['user'] = config.VARS.user
769 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
770 d['version'] = config.INTERNAL.sat_version
771 if 'profile' in config.APPLICATION:
772 d['profile'] = config.APPLICATION.profile.product
773 d['launcher'] = config.APPLICATION.profile.launcher_name
774 readme_template_path = os.path.join(config.VARS.internal_dir,
775 "README_SRC.template")
777 if package_type == PROJECT:
778 d['user'] = config.VARS.user
779 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
780 d['version'] = config.INTERNAL.sat_version
781 readme_template_path = os.path.join(config.VARS.internal_dir,
782 "README_PROJECT.template")
784 if package_type == SAT:
785 d['user'] = config.VARS.user
786 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
787 d['version'] = config.INTERNAL.sat_version
788 readme_template_path = os.path.join(config.VARS.internal_dir,
789 "README_SAT.template")
791 f.write(src.template.substitute(readme_template_path, d))
797 '''method that is called when salomeTools is called with --help option.
799 :return: The text to display for the package command description.
802 return _("The package command creates an archive.\nThere are 4 kinds of "
803 "archive:\n 1- The binary archive. It contains all the product "
804 "installation directories and a launcher,\n 2- The sources archive."
805 " It contains the products archives, a project corresponding to "
806 "the application and salomeTools,\n 3- The project archive. It "
807 "contains a project (give the project file path as argument),\n 4-"
808 " The salomeTools archive. It contains salomeTools.\n\nexample:"
809 "\nsat package SALOME-master --sources")
811 def run(args, runner, logger):
812 '''method that is called when salomeTools is called with package parameter.
816 (options, args) = parser.parse_args(args)
818 # Check that a type of package is called, and only one
819 all_option_types = (options.binaries,
821 options.project not in ["", None],
824 # Check if no option for package type
825 if all_option_types.count(True) == 0:
826 msg = _("Error: Precise a type for the package\nUse one of the "
827 "following options: --binaries, --sources, --project or"
829 logger.write(src.printcolors.printcError(msg), 1)
830 logger.write("\n", 1)
833 # Check for only one option for package type
834 if all_option_types.count(True) > 1:
835 msg = _("Error: You can use only one type for the package\nUse only one"
836 " of the following options: --binaries, --sources, --project or"
838 logger.write(src.printcolors.printcError(msg), 1)
839 logger.write("\n", 1)
842 # Get the package type
844 package_type = BINARY
846 package_type = SOURCE
848 package_type = PROJECT
852 # The repository where to put the package if not Binary or Source
853 package_default_path = runner.cfg.USER.workdir
855 if package_type in [BINARY, SOURCE]:
856 # Check that the command has been called with an application
857 src.check_config_has_application(runner.cfg)
859 # Display information
860 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
861 runner.cfg.VARS.application), 1)
863 # Get the default directory where to put the packages
864 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
866 src.ensure_path_exists(package_default_path)
868 elif package_type == PROJECT:
869 # check that the project is visible by SAT
870 if options.project not in runner.cfg.PROJECTS.project_file_paths:
871 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
874 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
875 "\nPlease add it in the %(site)s file." % {
876 "proj" : options.project, "site" : site_path})
877 logger.write(src.printcolors.printcError(msg), 1)
878 logger.write("\n", 1)
882 src.printcolors.print_value(logger, "Package type", package_type, 2)
884 # get the name of the archive or construct it
886 if os.path.basename(options.name) == options.name:
887 # only a name (not a path)
888 archive_name = options.name
889 dir_name = package_default_path
891 archive_name = os.path.basename(options.name)
892 dir_name = os.path.dirname(options.name)
895 if archive_name[-len(".tgz"):] == ".tgz":
896 archive_name = archive_name[:-len(".tgz")]
897 if archive_name[-len(".tar.gz"):] == ".tar.gz":
898 archive_name = archive_name[:-len(".tar.gz")]
901 dir_name = package_default_path
902 if package_type == BINARY:
903 archive_name = (runner.cfg.APPLICATION.name +
905 runner.cfg.VARS.dist)
907 if package_type == SOURCE:
908 archive_name = (runner.cfg.APPLICATION.name +
912 archive_name = (runner.cfg.APPLICATION.name +
918 if package_type == PROJECT:
919 project_name, __ = os.path.splitext(
920 os.path.basename(options.project))
921 archive_name = ("PROJECT" +
925 if package_type == SAT:
926 archive_name = ("salomeTools" +
928 runner.cfg.INTERNAL.sat_version)
930 path_targz = os.path.join(dir_name, archive_name + ".tgz")
932 # Print the path of the package
933 src.printcolors.print_value(logger, "Package path", path_targz, 2)
935 # Create a working directory for all files that are produced during the
936 # package creation and that will be removed at the end of the command
937 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
938 runner.cfg.VARS.datehour)
939 src.ensure_path_exists(tmp_working_dir)
940 logger.write("\n", 5)
941 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
943 logger.write("\n", 3)
945 msg = _("Preparation of files to add to the archive")
946 logger.write(src.printcolors.printcLabel(msg), 2)
947 logger.write("\n", 2)
949 if package_type == BINARY:
950 d_files_to_add = binary_package(runner.cfg,
954 if not(d_files_to_add):
957 if package_type == SOURCE:
958 d_files_to_add = source_package(runner,
964 if package_type == PROJECT:
965 d_files_to_add = project_package(options.project, tmp_working_dir)
967 if package_type == SAT:
968 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
970 # Add the README file in the package
971 local_readme_tmp_path = add_readme(runner.cfg,
974 d_files_to_add["README"] = (local_readme_tmp_path, "README")
976 # Add the additional files of option add_files
977 if options.add_files:
978 for file_path in options.add_files:
979 if not os.path.exists(file_path):
980 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
982 file_name = os.path.basename(file_path)
983 d_files_to_add[file_name] = (file_path, file_name)
985 logger.write("\n", 2)
987 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
988 logger.write("\n", 2)
991 # Creating the object tarfile
992 tar = tarfile.open(path_targz, mode='w:gz')
994 # Add the files to the tarfile object
995 res = add_files(tar, archive_name, d_files_to_add, logger)
997 except KeyboardInterrupt:
998 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
999 logger.write(_("Removing the temporary working directory ... "), 1)
1000 # remove the working directory
1001 shutil.rmtree(tmp_working_dir)
1002 logger.write(_("OK"), 1)
1003 logger.write(_("\n"), 1)
1006 # remove the working directory
1007 shutil.rmtree(tmp_working_dir)
1009 # Print again the path of the package
1010 logger.write("\n", 2)
1011 src.printcolors.print_value(logger, "Package path", path_targz, 2)