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
367 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
369 os.chdir(tmp_working_dir)
370 if os.path.lexists(tmp_satlink_path):
371 os.remove(tmp_satlink_path)
372 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
375 d_sat["sat link"] = (tmp_satlink_path, "sat")
377 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
379 def get_archives(config, logger):
380 '''Find all the products that are get using an archive and all the products
381 that are get using a vcs (git, cvs, svn) repository.
383 :param config Config: The global configuration.
384 :param logger Logger: the logging instance
385 :return: the dictionary {name_product :
386 (local path of its archive, path in the package of its archive )}
387 and the list of specific configuration corresponding to the vcs
391 # Get the list of product informations
392 l_products_name = config.APPLICATION.products.keys()
393 l_product_info = src.product.get_products_infos(l_products_name,
397 for p_name, p_info in l_product_info:
398 # ignore the native and fixed products
399 if (src.product.product_is_native(p_info)
400 or src.product.product_is_fixed(p_info)):
402 if p_info.get_source == "archive":
403 archive_path = p_info.archive_info.archive_name
404 archive_name = os.path.basename(archive_path)
406 l_pinfo_vcs.append((p_name, p_info))
408 d_archives[p_name] = (archive_path,
409 os.path.join(ARCHIVE_DIR, archive_name))
410 return d_archives, l_pinfo_vcs
412 def add_salomeTools(config, tmp_working_dir):
413 '''Prepare a version of salomeTools that has a specific site.pyconf file
414 configured for a source package.
416 :param config Config: The global configuration.
417 :param tmp_working_dir str: The temporary local directory containing some
418 specific directories or files needed in the
420 :return: The path to the local salomeTools directory to add in the package
423 # Copy sat in the temporary working directory
424 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
425 sat_running_path = src.Path(config.VARS.salometoolsway)
426 sat_running_path.copy(sat_tmp_path)
428 # Update the site.pyconf file that contains the path to the project
429 site_pyconf_name = "site.pyconf"
430 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
431 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
432 ff = open(site_pyconf_file, "w")
433 ff.write(SITE_TEMPLATE)
436 return sat_tmp_path.path
438 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
439 '''For sources package that require that all products are get using an
440 archive, one has to create some archive for the vcs products.
441 So this method calls the clean and source command of sat and then create
444 :param l_pinfo_vcs List: The list of specific configuration corresponding to
446 :param sat Sat: The Sat instance that can be called to clean and source the
448 :param config Config: The global configuration.
449 :param logger Logger: the logging instance
450 :param tmp_working_dir str: The temporary local directory containing some
451 specific directories or files needed in the
453 :return: the dictionary that stores all the archives to add in the source
454 package. {label : (path_on_local_machine, path_in_archive)}
457 # clean the source directory of all the vcs products, then use the source
458 # command and thus construct an archive that will not contain the patches
459 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
461 logger.write(_("clean sources\n"))
462 args_clean = config.VARS.application
463 args_clean += " --sources --products "
464 args_clean += ",".join(l_prod_names)
465 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
467 logger.write(_("get sources"))
468 args_source = config.VARS.application
469 args_source += " --products "
470 args_source += ",".join(l_prod_names)
471 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
473 # make the new archives
475 for pn, pinfo in l_pinfo_vcs:
476 path_archive = make_archive(pn, pinfo, tmp_working_dir)
477 d_archives_vcs[pn] = (path_archive,
478 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
479 return d_archives_vcs
481 def make_archive(prod_name, prod_info, where):
482 '''Create an archive of a product by searching its source directory.
484 :param prod_name str: The name of the product.
485 :param prod_info Config: The specific configuration corresponding to the
487 :param where str: The path of the repository where to put the resulting
489 :return: The path of the resulting archive
492 path_targz_prod = os.path.join(where, prod_name + ".tgz")
493 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
494 local_path = prod_info.source_dir
495 tar_prod.add(local_path, arcname=prod_name)
497 return path_targz_prod
499 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
500 '''Create a specific project for a source package.
502 :param config Config: The global configuration.
503 :param tmp_working_dir str: The temporary local directory containing some
504 specific directories or files needed in the
506 :param with_vcs boolean: True if the package is with vcs products (not
507 transformed into archive products)
508 :return: The dictionary
509 {"project" : (produced project, project path in the archive)}
513 # Create in the working temporary directory the full project tree
514 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
515 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
517 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
520 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
523 patches_tmp_dir = os.path.join(project_tmp_dir,
526 application_tmp_dir = os.path.join(project_tmp_dir,
528 for directory in [project_tmp_dir,
529 compil_scripts_tmp_dir,
532 application_tmp_dir]:
533 src.ensure_path_exists(directory)
535 # Create the pyconf that contains the information of the project
536 project_pyconf_name = "project.pyconf"
537 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
538 ff = open(project_pyconf_file, "w")
539 ff.write(PROJECT_TEMPLATE)
542 # Loop over the products to get there pyconf and all the scripts
543 # (compilation, environment, patches)
544 # and create the pyconf file to add to the project
545 lproducts_name = config.APPLICATION.products.keys()
546 l_products = src.product.get_products_infos(lproducts_name, config)
547 for p_name, p_info in l_products:
548 # ignore native and fixed products
549 if (src.product.product_is_native(p_info) or
550 src.product.product_is_fixed(p_info)):
552 find_product_scripts_and_pyconf(p_name,
556 compil_scripts_tmp_dir,
559 products_pyconf_tmp_dir)
561 find_application_pyconf(config, application_tmp_dir)
563 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
566 def find_product_scripts_and_pyconf(p_name,
570 compil_scripts_tmp_dir,
573 products_pyconf_tmp_dir):
574 '''Create a specific pyconf file for a given product. Get its environment
575 script, its compilation script and patches and put it in the temporary
576 working directory. This method is used in the source package in order to
577 construct the specific project.
579 :param p_name str: The name of the product.
580 :param p_info Config: The specific configuration corresponding to the
582 :param config Config: The global configuration.
583 :param with_vcs boolean: True if the package is with vcs products (not
584 transformed into archive products)
585 :param compil_scripts_tmp_dir str: The path to the temporary compilation
586 scripts directory of the project.
587 :param env_scripts_tmp_dir str: The path to the temporary environment script
588 directory of the project.
589 :param patches_tmp_dir str: The path to the temporary patch scripts
590 directory of the project.
591 :param products_pyconf_tmp_dir str: The path to the temporary product
592 scripts directory of the project.
595 # read the pyconf of the product
596 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
597 config.PATHS.PRODUCTPATH)
598 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
600 # find the compilation script if any
601 if src.product.product_has_script(p_info):
602 compil_script_path = src.Path(p_info.compil_script)
603 compil_script_path.copy(compil_scripts_tmp_dir)
604 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
605 p_info.compil_script)
606 # find the environment script if any
607 if src.product.product_has_env_script(p_info):
608 env_script_path = src.Path(p_info.environ.env_script)
609 env_script_path.copy(env_scripts_tmp_dir)
610 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
611 p_info.environ.env_script)
612 # find the patches if any
613 if src.product.product_has_patches(p_info):
614 patches = src.pyconf.Sequence()
615 for patch_path in p_info.patches:
616 p_path = src.Path(patch_path)
617 p_path.copy(patches_tmp_dir)
618 patches.append(os.path.basename(patch_path), "")
620 product_pyconf_cfg[p_info.section].patches = patches
623 # put in the pyconf file the resolved values
624 for info in ["git_info", "cvs_info", "svn_info"]:
626 for key in p_info[info]:
627 product_pyconf_cfg[p_info.section][info][key] = p_info[
630 # if the product is not archive, then make it become archive.
631 if src.product.product_is_vcs(p_info):
632 product_pyconf_cfg[p_info.section].get_source = "archive"
633 if not "archive_info" in product_pyconf_cfg[p_info.section]:
634 product_pyconf_cfg[p_info.section].addMapping("archive_info",
635 src.pyconf.Mapping(product_pyconf_cfg),
637 product_pyconf_cfg[p_info.section
638 ].archive_info.archive_name = p_info.name + ".tgz"
640 # write the pyconf file to the temporary project location
641 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
643 ff = open(product_tmp_pyconf_path, 'w')
644 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
645 product_pyconf_cfg.__save__(ff, 1)
648 def find_application_pyconf(config, application_tmp_dir):
649 '''Find the application pyconf file and put it in the specific temporary
650 directory containing the specific project of a source package.
652 :param config Config: The global configuration.
653 :param application_tmp_dir str: The path to the temporary application
654 scripts directory of the project.
656 # read the pyconf of the application
657 application_name = config.VARS.application
658 application_pyconf_path = src.find_file_in_lpath(
659 application_name + ".pyconf",
660 config.PATHS.APPLICATIONPATH)
661 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
664 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
665 application_pyconf_cfg,
667 'VARS.salometoolsway + $VARS.sep + ".."')
669 # Prevent from compilation in base
670 application_pyconf_cfg.APPLICATION.no_base = "yes"
672 # write the pyconf file to the temporary application location
673 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
674 application_name + ".pyconf")
675 ff = open(application_tmp_pyconf_path, 'w')
676 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
677 application_pyconf_cfg.__save__(ff, 1)
680 def project_package(project_file_path, tmp_working_dir):
681 '''Prepare a dictionary that stores all the needed directories and files to
682 add in a project package.
684 :param project_file_path str: The path to the local project.
685 :param tmp_working_dir str: The temporary local directory containing some
686 specific directories or files needed in the
688 :return: the dictionary that stores all the needed directories and files to
689 add in a project package.
690 {label : (path_on_local_machine, path_in_archive)}
694 # Read the project file and get the directories to add to the package
695 project_pyconf_cfg = src.pyconf.Config(project_file_path)
696 paths = {"ARCHIVEPATH" : "archives",
697 "APPLICATIONPATH" : "applications",
698 "PRODUCTPATH" : "products",
700 "MACHINEPATH" : "machines"}
701 # Loop over the project paths and add it
703 if path not in project_pyconf_cfg:
705 # Add the directory to the files to add in the package
706 d_project[path] = (project_pyconf_cfg[path], paths[path])
707 # Modify the value of the path in the package
708 project_pyconf_cfg[path] = src.pyconf.Reference(
711 'project_path + "/' + paths[path] + '"')
714 if "project_path" not in project_pyconf_cfg:
715 project_pyconf_cfg.addMapping("project_path",
716 src.pyconf.Mapping(project_pyconf_cfg),
718 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
722 # Write the project pyconf file
723 project_file_name = os.path.basename(project_file_path)
724 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
725 ff = open(project_pyconf_tmp_path, 'w')
726 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
727 project_pyconf_cfg.__save__(ff, 1)
729 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
733 def add_readme(config, package_type, where):
734 readme_path = os.path.join(where, "README")
735 f = open(readme_path, 'w')
736 # prepare substitution dictionary
738 if package_type == BINARY:
739 d['application'] = config.VARS.application
740 d['user'] = config.VARS.user
741 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
742 d['version'] = config.INTERNAL.sat_version
743 d['dist'] = config.VARS.dist
744 if 'profile' in config.APPLICATION:
745 d['launcher'] = config.APPLICATION.profile.launcher_name
746 readme_template_path = os.path.join(config.VARS.internal_dir,
747 "README_BIN.template")
748 if package_type == SOURCE:
749 d['application'] = config.VARS.application
750 d['user'] = config.VARS.user
751 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
752 d['version'] = config.INTERNAL.sat_version
753 if 'profile' in config.APPLICATION:
754 d['profile'] = config.APPLICATION.profile.product
755 d['launcher'] = config.APPLICATION.profile.launcher_name
756 readme_template_path = os.path.join(config.VARS.internal_dir,
757 "README_SRC.template")
759 if package_type == PROJECT:
760 d['user'] = config.VARS.user
761 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
762 d['version'] = config.INTERNAL.sat_version
763 readme_template_path = os.path.join(config.VARS.internal_dir,
764 "README_PROJECT.template")
766 if package_type == SAT:
767 d['user'] = config.VARS.user
768 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
769 d['version'] = config.INTERNAL.sat_version
770 readme_template_path = os.path.join(config.VARS.internal_dir,
771 "README_SAT.template")
773 f.write(src.template.substitute(readme_template_path, d))
779 '''method that is called when salomeTools is called with --help option.
781 :return: The text to display for the package command description.
784 return _("The package command creates an archive.\nThere are 4 kinds of "
785 "archive:\n 1- The binary archive. It contains all the product "
786 "installation directories and a launcher,\n 2- The sources archive."
787 " It contains the products archives, a project corresponding to "
788 "the application and salomeTools,\n 3- The project archive. It "
789 "contains a project (give the project file path as argument),\n 4-"
790 " The salomeTools archive. It contains salomeTools.\n\nexample:"
791 "\nsat package SALOME-master --sources")
793 def run(args, runner, logger):
794 '''method that is called when salomeTools is called with package parameter.
798 (options, args) = parser.parse_args(args)
800 # Check that a type of package is called, and only one
801 all_option_types = (options.binaries,
803 options.project not in ["", None],
806 # Check if no option for package type
807 if all_option_types.count(True) == 0:
808 msg = _("Error: Precise a type for the package\nUse one of the "
809 "following options: --binaries, --sources, --project or"
811 logger.write(src.printcolors.printcError(msg), 1)
812 logger.write("\n", 1)
815 # Check for only one option for package type
816 if all_option_types.count(True) > 1:
817 msg = _("Error: You can use only one type for the package\nUse only one"
818 " of the following options: --binaries, --sources, --project or"
820 logger.write(src.printcolors.printcError(msg), 1)
821 logger.write("\n", 1)
824 # Get the package type
826 package_type = BINARY
828 package_type = SOURCE
830 package_type = PROJECT
834 # The repository where to put the package if not Binary or Source
835 package_default_path = runner.cfg.USER.workdir
837 if package_type in [BINARY, SOURCE]:
838 # Check that the command has been called with an application
839 src.check_config_has_application(runner.cfg)
841 # Display information
842 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
843 runner.cfg.VARS.application), 1)
845 # Get the default directory where to put the packages
846 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
848 src.ensure_path_exists(package_default_path)
850 elif package_type == PROJECT:
851 # check that the project is visible by SAT
852 if options.project not in runner.cfg.PROJECTS.project_file_paths:
853 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
856 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
857 "\nPlease add it in the %(site)s file." % {
858 "proj" : options.project, "site" : site_path})
859 logger.write(src.printcolors.printcError(msg), 1)
860 logger.write("\n", 1)
864 src.printcolors.print_value(logger, "Package type", package_type, 2)
866 # get the name of the archive or construct it
868 if os.path.basename(options.name) == options.name:
869 # only a name (not a path)
870 archive_name = options.name
871 dir_name = package_default_path
873 archive_name = os.path.basename(options.name)
874 dir_name = os.path.dirname(options.name)
877 if archive_name[-len(".tgz"):] == ".tgz":
878 archive_name = archive_name[:-len(".tgz")]
879 if archive_name[-len(".tar.gz"):] == ".tar.gz":
880 archive_name = archive_name[:-len(".tar.gz")]
883 dir_name = package_default_path
884 if package_type == BINARY:
885 archive_name = (runner.cfg.APPLICATION.name +
887 runner.cfg.VARS.dist)
889 if package_type == SOURCE:
890 archive_name = (runner.cfg.APPLICATION.name +
894 archive_name = (runner.cfg.APPLICATION.name +
900 if package_type == PROJECT:
901 project_name, __ = os.path.splitext(
902 os.path.basename(options.project))
903 archive_name = ("PROJECT" +
907 if package_type == SAT:
908 archive_name = ("salomeTools" +
910 runner.cfg.INTERNAL.sat_version)
912 path_targz = os.path.join(dir_name, archive_name + ".tgz")
914 # Print the path of the package
915 src.printcolors.print_value(logger, "Package path", path_targz, 2)
917 # Create a working directory for all files that are produced during the
918 # package creation and that will be removed at the end of the command
919 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
920 runner.cfg.VARS.datehour)
921 src.ensure_path_exists(tmp_working_dir)
922 logger.write("\n", 5)
923 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
925 logger.write("\n", 3)
927 msg = _("Preparation of files to add to the archive")
928 logger.write(src.printcolors.printcLabel(msg), 2)
929 logger.write("\n", 2)
931 if package_type == BINARY:
932 d_files_to_add = binary_package(runner.cfg,
936 if not(d_files_to_add):
939 if package_type == SOURCE:
940 d_files_to_add = source_package(runner,
946 if package_type == PROJECT:
947 d_files_to_add = project_package(options.project, tmp_working_dir)
949 if package_type == SAT:
950 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
952 # Add the README file in the package
953 local_readme_tmp_path = add_readme(runner.cfg,
956 d_files_to_add["README"] = (local_readme_tmp_path, "README")
958 logger.write("\n", 2)
960 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
961 logger.write("\n", 2)
964 # Creating the object tarfile
965 tar = tarfile.open(path_targz, mode='w:gz')
967 # Add the files to the tarfile object
968 res = add_files(tar, archive_name, d_files_to_add, logger)
970 except KeyboardInterrupt:
971 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
972 logger.write(_("Removing the temporary working directory ... "), 1)
973 # remove the working directory
974 shutil.rmtree(tmp_working_dir)
975 logger.write(_("OK"), 1)
976 logger.write(_("\n"), 1)
979 # remove the working directory
980 shutil.rmtree(tmp_working_dir)
982 # Print again the path of the package
983 logger.write("\n", 2)
984 src.printcolors.print_value(logger, "Package path", path_targz, 2)