3 # Copyright (C) 2010-2012 CEA/DEN
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 ARCHIVE_DIR = "ARCHIVES"
33 PROJECT_DIR = "PROJECT"
35 PROJECT_TEMPLATE = """#!/usr/bin/env python
38 # The path to the archive root directory
39 root_path : $PWD + "/../"
41 project_path : $PWD + "/"
43 # Where to search the archives of the products
44 ARCHIVEPATH : $root_path + "ARCHIVES"
45 # Where to search the pyconf of the applications
46 APPLICATIONPATH : $project_path + "applications/"
47 # Where to search the pyconf of the products
48 PRODUCTPATH : $project_path + "products/"
49 # Where to search the pyconf of the jobs of the project
50 JOBPATH : $project_path + "jobs/"
51 # Where to search the pyconf of the machines of the project
52 MACHINEPATH : $project_path + "machines/"
55 SITE_TEMPLATE = ("""#!/usr/bin/env python
62 log_dir : $USER.workdir + "/LOGS"
65 tmp_dir_with_application : '/tmp' + $VARS.sep + $VARS.user + """
66 """$VARS.sep + $APPLICATION.name + $VARS.sep + 'test'
67 tmp_dir : '/tmp' + $VARS.sep + $VARS.user + $VARS.sep + 'test'
74 project_file_paths : [$VARS.salometoolsway + $VARS.sep + \"..\" + $VARS.sep"""
75 """ + \"""" + PROJECT_DIR + """\" + $VARS.sep + "project.pyconf"]
79 # Define all possible option for the package command : sat package <options>
80 parser = src.options.Options()
81 parser.add_option('b', 'binaries', 'boolean', 'binaries',
82 _('Optional: Produce a binary package.'), False)
83 parser.add_option('f', 'force_creation', 'boolean', 'force_creation',
84 _('Optional: Only binary package: produce the archive even if '
85 'there are some missing products.'), False)
86 parser.add_option('', 'with_sources', 'boolean', 'with_sources',
87 _('Optional: Only binary package: produce and and a source archive in the '
88 'binary package.'), False)
89 parser.add_option('s', 'sources', 'boolean', 'sources',
90 _('Optional: Produce a compilable archive of the sources of the '
91 'application.'), False)
92 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
93 _('Optional: Only source package: do not make archive of vcs products.'),
95 parser.add_option('p', 'project', 'string', 'project',
96 _('Optional: Produce an archive that contains a project.'), "")
97 parser.add_option('t', 'salometools', 'boolean', 'sat',
98 _('Optional: Produce an archive that contains salomeTools.'), False)
99 parser.add_option('n', 'name', 'string', 'name',
100 _('Optional: The name or full path of the archive.'), None)
102 def add_files(tar, name_archive, d_content, logger):
103 '''Create an archive containing all directories and files that are given in
104 the d_content argument.
106 :param tar tarfile: The tarfile instance used to make the archive.
107 :param name_archive str: The name of the archive to make.
108 :param d_content dict: The dictionary that contain all directories and files
109 to add in the archive.
111 (path_on_local_machine, path_in_archive)
112 :param logger Logger: the logging instance
113 :return: 0 if success, 1 if not.
116 # get the max length of the messages in order to make the display
117 max_len = len(max(d_content.keys(), key=len))
120 # loop over each directory or file stored in the d_content dictionary
121 for name in d_content.keys():
122 # display information
123 len_points = max_len - len(name)
124 logger.write(name + " " + len_points * "." + " ", 3)
125 # Get the local path and the path in archive
126 # of the directory or file to add
127 local_path, archive_path = d_content[name]
128 in_archive = os.path.join(name_archive, archive_path)
129 # Add it in the archive
131 tar.add(local_path, arcname=in_archive)
132 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
133 except Exception as e:
134 logger.write(src.printcolors.printcError(_("KO ")), 3)
135 logger.write(str(e), 3)
137 logger.write("\n", 3)
140 def produce_relative_launcher(config,
145 '''Create a specific SALOME launcher for the binary package. This launcher
148 :param config Config: The global configuration.
149 :param logger Logger: the logging instance
150 :param file_dir str: the directory where to put the launcher
151 :param file_name str: The launcher name
152 :param binaries_dir_name str: the name of the repository where the binaries
154 :return: the path of the produced launcher
158 # Get the launcher template
159 profile_install_dir = os.path.join(binaries_dir_name,
160 config.APPLICATION.profile.product)
161 withProfile = src.fileEnviron.withProfile
162 withProfile = withProfile.replace(
163 "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
164 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
165 withProfile = withProfile.replace(
166 "os.path.join( 'PROFILE_INSTALL_DIR'",
167 "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
169 before, after = withProfile.split(
170 "# here your local standalone environment\n")
172 # create an environment file writer
173 writer = src.environment.FileEnvWriter(config,
178 filepath = os.path.join(file_dir, file_name)
179 # open the file and write into it
180 launch_file = open(filepath, "w")
181 launch_file.write(before)
183 writer.write_cfgForPy_file(launch_file, for_package = binaries_dir_name)
184 launch_file.write(after)
187 # Little hack to put out_dir_Path outside the strings
188 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
190 # change the rights in order to make the file executable for everybody
202 def produce_relative_env_files(config,
206 '''Create some specific environment files for the binary package. These
207 files use relative paths.
209 :param config Config: The global configuration.
210 :param logger Logger: the logging instance
211 :param file_dir str: the directory where to put the files
212 :param binaries_dir_name str: the name of the repository where the binaries
214 :return: the list of path of the produced environment files
217 # create an environment file writer
218 writer = src.environment.FileEnvWriter(config,
224 filepath = writer.write_env_file("env_launch.sh",
227 for_package = binaries_dir_name)
229 # Little hack to put out_dir_Path as environment variable
230 src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
232 # change the rights in order to make the file executable for everybody
245 def binary_package(config, logger, options, tmp_working_dir):
246 '''Prepare a dictionary that stores all the needed directories and files to
247 add in a binary package.
249 :param config Config: The global configuration.
250 :param logger Logger: the logging instance
251 :param options OptResult: the options of the launched command
252 :param tmp_working_dir str: The temporary local directory containing some
253 specific directories or files needed in the
255 :return: the dictionary that stores all the needed directories and files to
256 add in a binary package.
257 {label : (path_on_local_machine, path_in_archive)}
261 # Get the list of product installation to add to the archive
262 l_products_name = config.APPLICATION.products.keys()
263 l_product_info = src.product.get_products_infos(l_products_name,
267 for prod_name, prod_info in l_product_info:
268 # ignore the native and fixed products
269 if (src.product.product_is_native(prod_info)
270 or src.product.product_is_fixed(prod_info)
271 or not src.product.product_compiles(prod_info)):
273 if src.product.check_installation(prod_info):
274 l_install_dir.append((prod_name, prod_info.install_dir))
276 l_not_installed.append(prod_name)
278 # Print warning or error if there are some missing products
279 if len(l_not_installed) > 0:
280 text_missing_prods = ""
281 for p_name in l_not_installed:
282 text_missing_prods += "-" + p_name + "\n"
283 if not options.force_creation:
284 msg = _("ERROR: there are missing products installations:")
285 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
290 msg = _("WARNING: there are missing products installations:")
291 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
295 # construct the name of the directory that will contain the binaries
296 binaries_dir_name = "BINARIES-" + config.VARS.dist
298 # construct the correlation table between the product names, there
299 # actual install directories and there install directory in archive
301 for prod_name, install_dir in l_install_dir:
302 path_in_archive = os.path.join(binaries_dir_name, prod_name)
303 d_products[prod_name] = (install_dir, path_in_archive)
305 # create the relative launcher and add it to the files to add
306 if "profile" in config.APPLICATION:
307 launcher_name = config.APPLICATION.profile.launcher_name
308 launcher_package = produce_relative_launcher(config,
314 d_products["launcher"] = (launcher_package, launcher_name)
316 # No profile, it means that there has to be some environment files
317 env_file = produce_relative_env_files(config,
322 d_products["environment file"] = (env_file, "env_launch.sh")
326 def source_package(sat, config, logger, options, tmp_working_dir):
327 '''Prepare a dictionary that stores all the needed directories and files to
328 add in a source package.
330 :param config Config: The global configuration.
331 :param logger Logger: the logging instance
332 :param options OptResult: the options of the launched command
333 :param tmp_working_dir str: The temporary local directory containing some
334 specific directories or files needed in the
336 :return: the dictionary that stores all the needed directories and files to
337 add in a source package.
338 {label : (path_on_local_machine, path_in_archive)}
342 # Get all the products that are prepared using an archive
343 logger.write("Find archive products ... ")
344 d_archives, l_pinfo_vcs = get_archives(config, logger)
345 logger.write("Done\n")
347 if not options.with_vcs and len(l_pinfo_vcs) > 0:
348 # Make archives with the products that are not prepared using an archive
349 # (git, cvs, svn, etc)
350 logger.write("Construct archives for vcs products ... ")
351 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
356 logger.write("Done\n")
359 logger.write("Create the project ... ")
360 d_project = create_project_for_src_package(config,
363 logger.write("Done\n")
366 tmp_sat = add_salomeTools(config, tmp_working_dir)
367 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
369 # Add a sat symbolic link
370 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
372 os.chdir(tmp_working_dir)
373 if os.path.lexists(tmp_satlink_path):
374 os.remove(tmp_satlink_path)
375 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
378 d_sat["sat link"] = (tmp_satlink_path, "sat")
380 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
382 def get_archives(config, logger):
383 '''Find all the products that are get using an archive and all the products
384 that are get using a vcs (git, cvs, svn) repository.
386 :param config Config: The global configuration.
387 :param logger Logger: the logging instance
388 :return: the dictionary {name_product :
389 (local path of its archive, path in the package of its archive )}
390 and the list of specific configuration corresponding to the vcs
394 # Get the list of product informations
395 l_products_name = config.APPLICATION.products.keys()
396 l_product_info = src.product.get_products_infos(l_products_name,
400 for p_name, p_info in l_product_info:
401 # ignore the native and fixed products
402 if (src.product.product_is_native(p_info)
403 or src.product.product_is_fixed(p_info)):
405 if p_info.get_source == "archive":
406 archive_path = p_info.archive_info.archive_name
407 archive_name = os.path.basename(archive_path)
409 l_pinfo_vcs.append((p_name, p_info))
411 d_archives[p_name] = (archive_path,
412 os.path.join(ARCHIVE_DIR, archive_name))
413 return d_archives, l_pinfo_vcs
415 def add_salomeTools(config, tmp_working_dir):
416 '''Prepare a version of salomeTools that has a specific site.pyconf file
417 configured for a source package.
419 :param config Config: The global configuration.
420 :param tmp_working_dir str: The temporary local directory containing some
421 specific directories or files needed in the
423 :return: The path to the local salomeTools directory to add in the package
426 # Copy sat in the temporary working directory
427 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
428 sat_running_path = src.Path(config.VARS.salometoolsway)
429 sat_running_path.copy(sat_tmp_path)
431 # Update the site.pyconf file that contains the path to the project
432 site_pyconf_name = "site.pyconf"
433 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
434 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
435 ff = open(site_pyconf_file, "w")
436 ff.write(SITE_TEMPLATE)
439 return sat_tmp_path.path
441 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
442 '''For sources package that require that all products are get using an
443 archive, one has to create some archive for the vcs products.
444 So this method calls the clean and source command of sat and then create
447 :param l_pinfo_vcs List: The list of specific configuration corresponding to
449 :param sat Sat: The Sat instance that can be called to clean and source the
451 :param config Config: The global configuration.
452 :param logger Logger: the logging instance
453 :param tmp_working_dir str: The temporary local directory containing some
454 specific directories or files needed in the
456 :return: the dictionary that stores all the archives to add in the source
457 package. {label : (path_on_local_machine, path_in_archive)}
460 # clean the source directory of all the vcs products, then use the source
461 # command and thus construct an archive that will not contain the patches
462 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
464 logger.write(_("clean sources\n"))
465 args_clean = config.VARS.application
466 args_clean += " --sources --products "
467 args_clean += ",".join(l_prod_names)
468 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
470 logger.write(_("get sources"))
471 args_source = config.VARS.application
472 args_source += " --products "
473 args_source += ",".join(l_prod_names)
474 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
476 # make the new archives
478 for pn, pinfo in l_pinfo_vcs:
479 path_archive = make_archive(pn, pinfo, tmp_working_dir)
480 d_archives_vcs[pn] = (path_archive,
481 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
482 return d_archives_vcs
484 def make_archive(prod_name, prod_info, where):
485 '''Create an archive of a product by searching its source directory.
487 :param prod_name str: The name of the product.
488 :param prod_info Config: The specific configuration corresponding to the
490 :param where str: The path of the repository where to put the resulting
492 :return: The path of the resulting archive
495 path_targz_prod = os.path.join(where, prod_name + ".tgz")
496 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
497 local_path = prod_info.source_dir
498 tar_prod.add(local_path, arcname=prod_name)
500 return path_targz_prod
502 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
503 '''Create a specific project for a source package.
505 :param config Config: The global configuration.
506 :param tmp_working_dir str: The temporary local directory containing some
507 specific directories or files needed in the
509 :param with_vcs boolean: True if the package is with vcs products (not
510 transformed into archive products)
511 :return: The dictionary
512 {"project" : (produced project, project path in the archive)}
516 # Create in the working temporary directory the full project tree
517 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
518 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
520 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
523 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
526 patches_tmp_dir = os.path.join(project_tmp_dir,
529 application_tmp_dir = os.path.join(project_tmp_dir,
531 for directory in [project_tmp_dir,
532 compil_scripts_tmp_dir,
535 application_tmp_dir]:
536 src.ensure_path_exists(directory)
538 # Create the pyconf that contains the information of the project
539 project_pyconf_name = "project.pyconf"
540 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
541 ff = open(project_pyconf_file, "w")
542 ff.write(PROJECT_TEMPLATE)
545 # Loop over the products to get there pyconf and all the scripts
546 # (compilation, environment, patches)
547 # and create the pyconf file to add to the project
548 lproducts_name = config.APPLICATION.products.keys()
549 l_products = src.product.get_products_infos(lproducts_name, config)
550 for p_name, p_info in l_products:
551 # ignore native and fixed products
552 if (src.product.product_is_native(p_info) or
553 src.product.product_is_fixed(p_info)):
555 find_product_scripts_and_pyconf(p_name,
559 compil_scripts_tmp_dir,
562 products_pyconf_tmp_dir)
564 find_application_pyconf(config, application_tmp_dir)
566 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
569 def find_product_scripts_and_pyconf(p_name,
573 compil_scripts_tmp_dir,
576 products_pyconf_tmp_dir):
577 '''Create a specific pyconf file for a given product. Get its environment
578 script, its compilation script and patches and put it in the temporary
579 working directory. This method is used in the source package in order to
580 construct the specific project.
582 :param p_name str: The name of the product.
583 :param p_info Config: The specific configuration corresponding to the
585 :param config Config: The global configuration.
586 :param with_vcs boolean: True if the package is with vcs products (not
587 transformed into archive products)
588 :param compil_scripts_tmp_dir str: The path to the temporary compilation
589 scripts directory of the project.
590 :param env_scripts_tmp_dir str: The path to the temporary environment script
591 directory of the project.
592 :param patches_tmp_dir str: The path to the temporary patch scripts
593 directory of the project.
594 :param products_pyconf_tmp_dir str: The path to the temporary product
595 scripts directory of the project.
598 # read the pyconf of the product
599 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
600 config.PATHS.PRODUCTPATH)
601 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
603 # find the compilation script if any
604 if src.product.product_has_script(p_info):
605 compil_script_path = src.Path(p_info.compil_script)
606 compil_script_path.copy(compil_scripts_tmp_dir)
607 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
608 p_info.compil_script)
609 # find the environment script if any
610 if src.product.product_has_env_script(p_info):
611 env_script_path = src.Path(p_info.environ.env_script)
612 env_script_path.copy(env_scripts_tmp_dir)
613 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
614 p_info.environ.env_script)
615 # find the patches if any
616 if src.product.product_has_patches(p_info):
617 patches = src.pyconf.Sequence()
618 for patch_path in p_info.patches:
619 p_path = src.Path(patch_path)
620 p_path.copy(patches_tmp_dir)
621 patches.append(os.path.basename(patch_path), "")
623 product_pyconf_cfg[p_info.section].patches = patches
626 # put in the pyconf file the resolved values
627 for info in ["git_info", "cvs_info", "svn_info"]:
629 for key in p_info[info]:
630 product_pyconf_cfg[p_info.section][info][key] = p_info[
633 # if the product is not archive, then make it become archive.
634 if src.product.product_is_vcs(p_info):
635 product_pyconf_cfg[p_info.section].get_source = "archive"
636 if not "archive_info" in product_pyconf_cfg[p_info.section]:
637 product_pyconf_cfg[p_info.section].addMapping("archive_info",
638 src.pyconf.Mapping(product_pyconf_cfg),
640 product_pyconf_cfg[p_info.section
641 ].archive_info.archive_name = p_info.name + ".tgz"
643 # write the pyconf file to the temporary project location
644 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
646 ff = open(product_tmp_pyconf_path, 'w')
647 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
648 product_pyconf_cfg.__save__(ff, 1)
651 def find_application_pyconf(config, application_tmp_dir):
652 '''Find the application pyconf file and put it in the specific temporary
653 directory containing the specific project of a source package.
655 :param config Config: The global configuration.
656 :param application_tmp_dir str: The path to the temporary application
657 scripts directory of the project.
659 # read the pyconf of the application
660 application_name = config.VARS.application
661 application_pyconf_path = src.find_file_in_lpath(
662 application_name + ".pyconf",
663 config.PATHS.APPLICATIONPATH)
664 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
667 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
668 application_pyconf_cfg,
670 'VARS.salometoolsway + $VARS.sep + ".."')
672 # Prevent from compilation in base
673 application_pyconf_cfg.APPLICATION.no_base = "yes"
675 # write the pyconf file to the temporary application location
676 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
677 application_name + ".pyconf")
678 ff = open(application_tmp_pyconf_path, 'w')
679 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
680 application_pyconf_cfg.__save__(ff, 1)
683 def project_package(project_file_path, tmp_working_dir):
684 '''Prepare a dictionary that stores all the needed directories and files to
685 add in a project package.
687 :param project_file_path str: The path to the local project.
688 :param tmp_working_dir str: The temporary local directory containing some
689 specific directories or files needed in the
691 :return: the dictionary that stores all the needed directories and files to
692 add in a project package.
693 {label : (path_on_local_machine, path_in_archive)}
697 # Read the project file and get the directories to add to the package
698 project_pyconf_cfg = src.pyconf.Config(project_file_path)
699 paths = {"ARCHIVEPATH" : "archives",
700 "APPLICATIONPATH" : "applications",
701 "PRODUCTPATH" : "products",
703 "MACHINEPATH" : "machines"}
704 # Loop over the project paths and add it
706 if path not in project_pyconf_cfg:
708 # Add the directory to the files to add in the package
709 d_project[path] = (project_pyconf_cfg[path], paths[path])
710 # Modify the value of the path in the package
711 project_pyconf_cfg[path] = src.pyconf.Reference(
714 'project_path + "/' + paths[path] + '"')
717 if "project_path" not in project_pyconf_cfg:
718 project_pyconf_cfg.addMapping("project_path",
719 src.pyconf.Mapping(project_pyconf_cfg),
721 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
725 # Write the project pyconf file
726 project_file_name = os.path.basename(project_file_path)
727 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
728 ff = open(project_pyconf_tmp_path, 'w')
729 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
730 project_pyconf_cfg.__save__(ff, 1)
732 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
736 def add_readme(config, package_type, where):
737 readme_path = os.path.join(where, "README")
738 f = open(readme_path, 'w')
739 # prepare substitution dictionary
741 if package_type == BINARY:
742 d['application'] = config.VARS.application
743 d['user'] = config.VARS.user
744 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
745 d['version'] = config.INTERNAL.sat_version
746 d['dist'] = config.VARS.dist
747 if 'profile' in config.APPLICATION:
748 d['launcher'] = config.APPLICATION.profile.launcher_name
749 readme_template_path = os.path.join(config.VARS.internal_dir,
750 "README_BIN.template")
751 if package_type == SOURCE:
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 if 'profile' in config.APPLICATION:
757 d['profile'] = config.APPLICATION.profile.product
758 d['launcher'] = config.APPLICATION.profile.launcher_name
759 readme_template_path = os.path.join(config.VARS.internal_dir,
760 "README_SRC.template")
762 if package_type == PROJECT:
763 d['user'] = config.VARS.user
764 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
765 d['version'] = config.INTERNAL.sat_version
766 readme_template_path = os.path.join(config.VARS.internal_dir,
767 "README_PROJECT.template")
769 if package_type == SAT:
770 d['user'] = config.VARS.user
771 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
772 d['version'] = config.INTERNAL.sat_version
773 readme_template_path = os.path.join(config.VARS.internal_dir,
774 "README_SAT.template")
776 f.write(src.template.substitute(readme_template_path, d))
782 '''method that is called when salomeTools is called with --help option.
784 :return: The text to display for the package command description.
787 return _("The package command creates an archive.\nThere are 4 kinds of "
788 "archive:\n 1- The binary archive. It contains all the product "
789 "installation directories and a launcher,\n 2- The sources archive."
790 " It contains the products archives, a project corresponding to "
791 "the application and salomeTools,\n 3- The project archive. It "
792 "contains a project (give the project file path as argument),\n 4-"
793 " The salomeTools archive. It contains salomeTools.\n\nexample:"
794 "\nsat package SALOME-master --sources")
796 def run(args, runner, logger):
797 '''method that is called when salomeTools is called with package parameter.
801 (options, args) = parser.parse_args(args)
803 # Check that a type of package is called, and only one
804 all_option_types = (options.binaries,
806 options.project not in ["", None],
809 # Check if no option for package type
810 if all_option_types.count(True) == 0:
811 msg = _("Error: Precise a type for the package\nUse one of the "
812 "following options: --binaries, --sources, --project or"
814 logger.write(src.printcolors.printcError(msg), 1)
815 logger.write("\n", 1)
818 # Check for only one option for package type
819 if all_option_types.count(True) > 1:
820 msg = _("Error: You can use only one type for the package\nUse only one"
821 " of the following options: --binaries, --sources, --project or"
823 logger.write(src.printcolors.printcError(msg), 1)
824 logger.write("\n", 1)
827 # Get the package type
829 package_type = BINARY
831 package_type = SOURCE
833 package_type = PROJECT
837 # The repository where to put the package if not Binary or Source
838 package_default_path = runner.cfg.USER.workdir
840 if package_type in [BINARY, SOURCE]:
841 # Check that the command has been called with an application
842 src.check_config_has_application(runner.cfg)
844 # Display information
845 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
846 runner.cfg.VARS.application), 1)
848 # Get the default directory where to put the packages
849 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
851 src.ensure_path_exists(package_default_path)
853 elif package_type == PROJECT:
854 # check that the project is visible by SAT
855 if options.project not in runner.cfg.PROJECTS.project_file_paths:
856 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
859 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
860 "\nPlease add it in the %(site)s file." % {
861 "proj" : options.project, "site" : site_path})
862 logger.write(src.printcolors.printcError(msg), 1)
863 logger.write("\n", 1)
867 src.printcolors.print_value(logger, "Package type", package_type, 2)
869 # get the name of the archive or construct it
871 if os.path.basename(options.name) == options.name:
872 # only a name (not a path)
873 archive_name = options.name
874 dir_name = package_default_path
876 archive_name = os.path.basename(options.name)
877 dir_name = os.path.dirname(options.name)
880 if archive_name[-len(".tgz"):] == ".tgz":
881 archive_name = archive_name[:-len(".tgz")]
882 if archive_name[-len(".tar.gz"):] == ".tar.gz":
883 archive_name = archive_name[:-len(".tar.gz")]
886 dir_name = package_default_path
887 if package_type == BINARY:
888 archive_name = (runner.cfg.APPLICATION.name +
890 runner.cfg.VARS.dist)
892 if package_type == SOURCE:
893 archive_name = (runner.cfg.APPLICATION.name +
897 archive_name = (runner.cfg.APPLICATION.name +
903 if package_type == PROJECT:
904 project_name, __ = os.path.splitext(
905 os.path.basename(options.project))
906 archive_name = ("PROJECT" +
910 if package_type == SAT:
911 archive_name = ("salomeTools" +
913 runner.cfg.INTERNAL.sat_version)
915 path_targz = os.path.join(dir_name, archive_name + ".tgz")
917 # Print the path of the package
918 src.printcolors.print_value(logger, "Package path", path_targz, 2)
920 # Create a working directory for all files that are produced during the
921 # package creation and that will be removed at the end of the command
922 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
923 runner.cfg.VARS.datehour)
924 src.ensure_path_exists(tmp_working_dir)
925 logger.write("\n", 5)
926 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
928 logger.write("\n", 3)
930 msg = _("Preparation of files to add to the archive")
931 logger.write(src.printcolors.printcLabel(msg), 2)
932 logger.write("\n", 2)
934 if package_type == BINARY:
935 d_files_to_add = binary_package(runner.cfg,
939 if not(d_files_to_add):
942 # Create and add the source package
943 # if the option "with_sources" is called
944 if options.with_sources:
945 logger.write(_("Create a source archive (can be long) ... "), 3)
946 tmp_pkg_src_name = runner.cfg.APPLICATION.name + "-" + "SRC.tgz"
947 tmp_pkg_src_path = os.path.join(tmp_working_dir, tmp_pkg_src_name)
948 package_options = runner.cfg.VARS.application
949 package_options += " --sources --with_vcs --name "
950 package_options += tmp_pkg_src_path
951 # sat package <package_options>
952 runner.package(package_options,
955 logger_add_link = logger)
956 d_files_to_add["SOURCES PACKAGE"] = (tmp_pkg_src_path,
958 logger.write(src.printcolors.printc("OK"), 3)
959 logger.write("\n", 3)
961 if package_type == SOURCE:
962 d_files_to_add = source_package(runner,
968 if package_type == PROJECT:
969 d_files_to_add = project_package(options.project, tmp_working_dir)
971 if package_type == SAT:
972 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
974 # Add the README file in the package
975 local_readme_tmp_path = add_readme(runner.cfg,
978 d_files_to_add["README"] = (local_readme_tmp_path, "README")
980 logger.write("\n", 2)
982 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
983 logger.write("\n", 2)
986 # Creating the object tarfile
987 tar = tarfile.open(path_targz, mode='w:gz')
989 # Add the files to the tarfile object
990 res = add_files(tar, archive_name, d_files_to_add, logger)
992 except KeyboardInterrupt:
993 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
994 logger.write(_("Removing the temporary working directory ... "), 1)
995 # remove the working directory
996 shutil.rmtree(tmp_working_dir)
997 logger.write(_("OK"), 1)
998 logger.write(_("\n"), 1)
1001 # remove the working directory
1002 shutil.rmtree(tmp_working_dir)
1004 # Print again the path of the package
1005 logger.write("\n", 2)
1006 src.printcolors.print_value(logger, "Package path", path_targz, 2)