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
27 from application import get_SALOME_modules
34 ARCHIVE_DIR = "ARCHIVES"
35 PROJECT_DIR = "PROJECT"
37 PROJECT_TEMPLATE = """#!/usr/bin/env python
40 # The path to the archive root directory
41 root_path : $PWD + "/../"
43 project_path : $PWD + "/"
45 # Where to search the archives of the products
46 ARCHIVEPATH : $root_path + "ARCHIVES"
47 # Where to search the pyconf of the applications
48 APPLICATIONPATH : $project_path + "applications/"
49 # Where to search the pyconf of the products
50 PRODUCTPATH : $project_path + "products/"
51 # Where to search the pyconf of the jobs of the project
52 JOBPATH : $project_path + "jobs/"
53 # Where to search the pyconf of the machines of the project
54 MACHINEPATH : $project_path + "machines/"
57 SITE_TEMPLATE = ("""#!/usr/bin/env python
64 log_dir : $USER.workdir + "/LOGS"
67 tmp_dir_with_application : '/tmp' + $VARS.sep + $VARS.user + """
68 """$VARS.sep + $APPLICATION.name + $VARS.sep + 'test'
69 tmp_dir : '/tmp' + $VARS.sep + $VARS.user + $VARS.sep + 'test'
76 project_file_paths : [$VARS.salometoolsway + $VARS.sep + \"..\" + $VARS.sep"""
77 """ + \"""" + PROJECT_DIR + """\" + $VARS.sep + "project.pyconf"]
81 # Define all possible option for the package command : sat package <options>
82 parser = src.options.Options()
83 parser.add_option('b', 'binaries', 'boolean', 'binaries',
84 _('Optional: Produce a binary package.'), False)
85 parser.add_option('f', 'force_creation', 'boolean', 'force_creation',
86 _('Optional: Only binary package: produce the archive even if '
87 'there are some missing products.'), False)
88 parser.add_option('s', 'sources', 'boolean', 'sources',
89 _('Optional: Produce a compilable archive of the sources of the '
90 'application.'), False)
91 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
92 _('Optional: Only source package: do not make archive of vcs products.'),
94 parser.add_option('p', 'project', 'string', 'project',
95 _('Optional: Produce an archive that contains a project.'), "")
96 parser.add_option('t', 'salometools', 'boolean', 'sat',
97 _('Optional: Produce an archive that contains salomeTools.'), False)
98 parser.add_option('n', 'name', 'string', 'name',
99 _('Optional: The name or full path of the archive.'), None)
100 parser.add_option('', 'add_files', 'list2', 'add_files',
101 _('Optional: The list of additional files to add to the archive.'), [])
102 parser.add_option('', 'without_commercial', 'boolean', 'without_commercial',
103 _('Optional: do not add commercial licence.'), False)
105 def add_files(tar, name_archive, d_content, logger):
106 '''Create an archive containing all directories and files that are given in
107 the d_content argument.
109 :param tar tarfile: The tarfile instance used to make the archive.
110 :param name_archive str: The name of the archive to make.
111 :param d_content dict: The dictionary that contain all directories and files
112 to add in the archive.
114 (path_on_local_machine, path_in_archive)
115 :param logger Logger: the logging instance
116 :return: 0 if success, 1 if not.
119 # get the max length of the messages in order to make the display
120 max_len = len(max(d_content.keys(), key=len))
123 # loop over each directory or file stored in the d_content dictionary
124 for name in d_content.keys():
125 # display information
126 len_points = max_len - len(name)
127 logger.write(name + " " + len_points * "." + " ", 3)
128 # Get the local path and the path in archive
129 # of the directory or file to add
130 local_path, archive_path = d_content[name]
131 in_archive = os.path.join(name_archive, archive_path)
132 # Add it in the archive
134 tar.add(local_path, arcname=in_archive)
135 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
136 except Exception as e:
137 logger.write(src.printcolors.printcError(_("KO ")), 3)
138 logger.write(str(e), 3)
140 logger.write("\n", 3)
143 def produce_relative_launcher(config,
148 with_commercial=True):
149 '''Create a specific SALOME launcher for the binary package. This launcher
152 :param config Config: The global configuration.
153 :param logger Logger: the logging instance
154 :param file_dir str: the directory where to put the launcher
155 :param file_name str: The launcher name
156 :param binaries_dir_name str: the name of the repository where the binaries
158 :return: the path of the produced launcher
162 # Get the launcher template
163 profile_install_dir = os.path.join(binaries_dir_name,
164 config.APPLICATION.profile.product)
165 withProfile = src.fileEnviron.withProfile
166 withProfile = withProfile.replace(
167 "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
168 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
169 withProfile = withProfile.replace(
170 "os.path.join( 'PROFILE_INSTALL_DIR'",
171 "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
173 before, after = withProfile.split(
174 "# here your local standalone environment\n")
176 # create an environment file writer
177 writer = src.environment.FileEnvWriter(config,
182 filepath = os.path.join(file_dir, file_name)
183 # open the file and write into it
184 launch_file = open(filepath, "w")
185 launch_file.write(before)
187 writer.write_cfgForPy_file(launch_file,
188 for_package = binaries_dir_name,
189 with_commercial=with_commercial)
190 launch_file.write(after)
193 # Little hack to put out_dir_Path outside the strings
194 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
196 # change the rights in order to make the file executable for everybody
208 def produce_relative_env_files(config,
212 '''Create some specific environment files for the binary package. These
213 files use relative paths.
215 :param config Config: The global configuration.
216 :param logger Logger: the logging instance
217 :param file_dir str: the directory where to put the files
218 :param binaries_dir_name str: the name of the repository where the binaries
220 :return: the list of path of the produced environment files
223 # create an environment file writer
224 writer = src.environment.FileEnvWriter(config,
230 filepath = writer.write_env_file("env_launch.sh",
233 for_package = binaries_dir_name)
235 # Little hack to put out_dir_Path as environment variable
236 src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
238 # change the rights in order to make the file executable for everybody
250 def product_appli_creation_script(config,
254 '''Create a script that can produce an application (EDF style) in the binary
257 :param config Config: The global configuration.
258 :param logger Logger: the logging instance
259 :param file_dir str: the directory where to put the file
260 :param binaries_dir_name str: the name of the repository where the binaries
262 :return: the path of the produced script file
265 template_name = "create_appli.py.for_bin_packages.template"
266 template_path = os.path.join(config.VARS.internal_dir, template_name)
267 text_to_fill = open(template_path, "r").read()
268 text_to_fill = text_to_fill.replace("TO BE FILLED 1",
269 '"' + binaries_dir_name + '"')
272 for product_name in get_SALOME_modules(config):
273 product_info = src.product.get_product_config(config, product_name)
275 if src.product.product_is_smesh_plugin(product_info):
278 if 'install_dir' in product_info and bool(product_info.install_dir):
279 if src.product.product_is_cpp(product_info):
281 for cpp_name in src.product.get_product_components(product_info):
282 line_to_add = ("<module name=\"" +
284 "\" gui=\"yes\" path=\"''' + "
285 "os.path.join(dir_bin_name, \"" +
286 cpp_name + "\") + '''\"/>")
289 line_to_add = ("<module name=\"" +
291 "\" gui=\"yes\" path=\"''' + "
292 "os.path.join(dir_bin_name, \"" +
293 product_name + "\") + '''\"/>")
294 text_to_add += line_to_add + "\n"
296 filled_text = text_to_fill.replace("TO BE FILLED 2", text_to_add)
298 tmp_file_path = os.path.join(file_dir, "create_appli.py")
299 ff = open(tmp_file_path, "w")
300 ff.write(filled_text)
303 # change the rights in order to make the file executable for everybody
304 os.chmod(tmp_file_path,
315 def binary_package(config, logger, options, tmp_working_dir):
316 '''Prepare a dictionary that stores all the needed directories and files to
317 add in a binary package.
319 :param config Config: The global configuration.
320 :param logger Logger: the logging instance
321 :param options OptResult: the options of the launched command
322 :param tmp_working_dir str: The temporary local directory containing some
323 specific directories or files needed in the
325 :return: the dictionary that stores all the needed directories and files to
326 add in a binary package.
327 {label : (path_on_local_machine, path_in_archive)}
331 # Get the list of product installation to add to the archive
332 l_products_name = config.APPLICATION.products.keys()
333 l_product_info = src.product.get_products_infos(l_products_name,
337 for prod_name, prod_info in l_product_info:
338 # ignore the native and fixed products
339 if (src.product.product_is_native(prod_info)
340 or src.product.product_is_fixed(prod_info)
341 or not src.product.product_compiles(prod_info)):
343 if src.product.check_installation(prod_info):
344 l_install_dir.append((prod_name, prod_info.install_dir))
346 l_not_installed.append(prod_name)
348 # Add also the cpp generated modules (if any)
349 if src.product.product_is_cpp(prod_info):
351 for name_cpp in src.product.get_product_components(prod_info):
352 install_dir = os.path.join(config.APPLICATION.workdir,
354 if os.path.exists(install_dir):
355 l_install_dir.append((name_cpp, install_dir))
357 l_not_installed.append(name_cpp)
359 # Print warning or error if there are some missing products
360 if len(l_not_installed) > 0:
361 text_missing_prods = ""
362 for p_name in l_not_installed:
363 text_missing_prods += "-" + p_name + "\n"
364 if not options.force_creation:
365 msg = _("ERROR: there are missing products installations:")
366 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
371 msg = _("WARNING: there are missing products installations:")
372 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
376 # construct the name of the directory that will contain the binaries
377 binaries_dir_name = "BINARIES-" + config.VARS.dist
379 # construct the correlation table between the product names, there
380 # actual install directories and there install directory in archive
382 for prod_name, install_dir in l_install_dir:
383 path_in_archive = os.path.join(binaries_dir_name, prod_name)
384 d_products[prod_name] = (install_dir, path_in_archive)
386 # create the relative launcher and add it to the files to add
387 if ("profile" in config.APPLICATION and
388 "product" in config.APPLICATION.profile):
389 launcher_name = config.APPLICATION.profile.launcher_name
390 launcher_package = produce_relative_launcher(config,
395 not(options.without_commercial))
397 d_products["launcher"] = (launcher_package, launcher_name)
399 # No profile, it means that there has to be some environment files
400 env_file = produce_relative_env_files(config,
405 d_products["environment file"] = (env_file, "env_launch.sh")
407 # And provide a script for the creation of an application EDF style
408 appli_script = product_appli_creation_script(config,
413 d_products["appli script"] = (appli_script, "create_appli.py")
417 def source_package(sat, config, logger, options, tmp_working_dir):
418 '''Prepare a dictionary that stores all the needed directories and files to
419 add in a source package.
421 :param config Config: The global configuration.
422 :param logger Logger: the logging instance
423 :param options OptResult: the options of the launched command
424 :param tmp_working_dir str: The temporary local directory containing some
425 specific directories or files needed in the
427 :return: the dictionary that stores all the needed directories and files to
428 add in a source package.
429 {label : (path_on_local_machine, path_in_archive)}
433 # Get all the products that are prepared using an archive
434 logger.write("Find archive products ... ")
435 d_archives, l_pinfo_vcs = get_archives(config, logger)
436 logger.write("Done\n")
438 if not options.with_vcs and len(l_pinfo_vcs) > 0:
439 # Make archives with the products that are not prepared using an archive
440 # (git, cvs, svn, etc)
441 logger.write("Construct archives for vcs products ... ")
442 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
447 logger.write("Done\n")
450 logger.write("Create the project ... ")
451 d_project = create_project_for_src_package(config,
454 logger.write("Done\n")
457 tmp_sat = add_salomeTools(config, tmp_working_dir)
458 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
460 # Add a sat symbolic link if not win
461 if not src.architecture.is_windows():
462 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
466 # In the jobs, os.getcwd() can fail
467 t = config.USER.workdir
468 os.chdir(tmp_working_dir)
469 if os.path.lexists(tmp_satlink_path):
470 os.remove(tmp_satlink_path)
471 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
474 d_sat["sat link"] = (tmp_satlink_path, "sat")
476 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
478 def get_archives(config, logger):
479 '''Find all the products that are get using an archive and all the products
480 that are get using a vcs (git, cvs, svn) repository.
482 :param config Config: The global configuration.
483 :param logger Logger: the logging instance
484 :return: the dictionary {name_product :
485 (local path of its archive, path in the package of its archive )}
486 and the list of specific configuration corresponding to the vcs
490 # Get the list of product informations
491 l_products_name = config.APPLICATION.products.keys()
492 l_product_info = src.product.get_products_infos(l_products_name,
496 for p_name, p_info in l_product_info:
497 # ignore the native and fixed products
498 if (src.product.product_is_native(p_info)
499 or src.product.product_is_fixed(p_info)):
501 if p_info.get_source == "archive":
502 archive_path = p_info.archive_info.archive_name
503 archive_name = os.path.basename(archive_path)
505 l_pinfo_vcs.append((p_name, p_info))
507 d_archives[p_name] = (archive_path,
508 os.path.join(ARCHIVE_DIR, archive_name))
509 return d_archives, l_pinfo_vcs
511 def add_salomeTools(config, tmp_working_dir):
512 '''Prepare a version of salomeTools that has a specific site.pyconf file
513 configured 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 :return: The path to the local salomeTools directory to add in the package
522 # Copy sat in the temporary working directory
523 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
524 sat_running_path = src.Path(config.VARS.salometoolsway)
525 sat_running_path.copy(sat_tmp_path)
527 # Update the site.pyconf file that contains the path to the project
528 site_pyconf_name = "site.pyconf"
529 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
530 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
531 ff = open(site_pyconf_file, "w")
532 ff.write(SITE_TEMPLATE)
535 return sat_tmp_path.path
537 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
538 '''For sources package that require that all products are get using an
539 archive, one has to create some archive for the vcs products.
540 So this method calls the clean and source command of sat and then create
543 :param l_pinfo_vcs List: The list of specific configuration corresponding to
545 :param sat Sat: The Sat instance that can be called to clean and source the
547 :param config Config: The global configuration.
548 :param logger Logger: the logging instance
549 :param tmp_working_dir str: The temporary local directory containing some
550 specific directories or files needed in the
552 :return: the dictionary that stores all the archives to add in the source
553 package. {label : (path_on_local_machine, path_in_archive)}
556 # clean the source directory of all the vcs products, then use the source
557 # command and thus construct an archive that will not contain the patches
558 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
560 logger.write(_("clean sources\n"))
561 args_clean = config.VARS.application
562 args_clean += " --sources --products "
563 args_clean += ",".join(l_prod_names)
564 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
566 logger.write(_("get sources"))
567 args_source = config.VARS.application
568 args_source += " --products "
569 args_source += ",".join(l_prod_names)
570 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
572 # make the new archives
574 for pn, pinfo in l_pinfo_vcs:
575 path_archive = make_archive(pn, pinfo, tmp_working_dir)
576 d_archives_vcs[pn] = (path_archive,
577 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
578 return d_archives_vcs
580 def make_archive(prod_name, prod_info, where):
581 '''Create an archive of a product by searching its source directory.
583 :param prod_name str: The name of the product.
584 :param prod_info Config: The specific configuration corresponding to the
586 :param where str: The path of the repository where to put the resulting
588 :return: The path of the resulting archive
591 path_targz_prod = os.path.join(where, prod_name + ".tgz")
592 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
593 local_path = prod_info.source_dir
594 tar_prod.add(local_path, arcname=prod_name)
596 return path_targz_prod
598 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
599 '''Create a specific project for a source package.
601 :param config Config: The global configuration.
602 :param tmp_working_dir str: The temporary local directory containing some
603 specific directories or files needed in the
605 :param with_vcs boolean: True if the package is with vcs products (not
606 transformed into archive products)
607 :return: The dictionary
608 {"project" : (produced project, project path in the archive)}
612 # Create in the working temporary directory the full project tree
613 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
614 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
616 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
619 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
622 patches_tmp_dir = os.path.join(project_tmp_dir,
625 application_tmp_dir = os.path.join(project_tmp_dir,
627 for directory in [project_tmp_dir,
628 compil_scripts_tmp_dir,
631 application_tmp_dir]:
632 src.ensure_path_exists(directory)
634 # Create the pyconf that contains the information of the project
635 project_pyconf_name = "project.pyconf"
636 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
637 ff = open(project_pyconf_file, "w")
638 ff.write(PROJECT_TEMPLATE)
641 # Loop over the products to get there pyconf and all the scripts
642 # (compilation, environment, patches)
643 # and create the pyconf file to add to the project
644 lproducts_name = config.APPLICATION.products.keys()
645 l_products = src.product.get_products_infos(lproducts_name, config)
646 for p_name, p_info in l_products:
647 # ignore native and fixed products
648 if (src.product.product_is_native(p_info) or
649 src.product.product_is_fixed(p_info)):
651 find_product_scripts_and_pyconf(p_name,
655 compil_scripts_tmp_dir,
658 products_pyconf_tmp_dir)
660 find_application_pyconf(config, application_tmp_dir)
662 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
665 def find_product_scripts_and_pyconf(p_name,
669 compil_scripts_tmp_dir,
672 products_pyconf_tmp_dir):
673 '''Create a specific pyconf file for a given product. Get its environment
674 script, its compilation script and patches and put it in the temporary
675 working directory. This method is used in the source package in order to
676 construct the specific project.
678 :param p_name str: The name of the product.
679 :param p_info Config: The specific configuration corresponding to the
681 :param config Config: The global configuration.
682 :param with_vcs boolean: True if the package is with vcs products (not
683 transformed into archive products)
684 :param compil_scripts_tmp_dir str: The path to the temporary compilation
685 scripts directory of the project.
686 :param env_scripts_tmp_dir str: The path to the temporary environment script
687 directory of the project.
688 :param patches_tmp_dir str: The path to the temporary patch scripts
689 directory of the project.
690 :param products_pyconf_tmp_dir str: The path to the temporary product
691 scripts directory of the project.
694 # read the pyconf of the product
695 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
696 config.PATHS.PRODUCTPATH)
697 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
699 # find the compilation script if any
700 if src.product.product_has_script(p_info):
701 compil_script_path = src.Path(p_info.compil_script)
702 compil_script_path.copy(compil_scripts_tmp_dir)
703 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
704 p_info.compil_script)
705 # find the environment script if any
706 if src.product.product_has_env_script(p_info):
707 env_script_path = src.Path(p_info.environ.env_script)
708 env_script_path.copy(env_scripts_tmp_dir)
709 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
710 p_info.environ.env_script)
711 # find the patches if any
712 if src.product.product_has_patches(p_info):
713 patches = src.pyconf.Sequence()
714 for patch_path in p_info.patches:
715 p_path = src.Path(patch_path)
716 p_path.copy(patches_tmp_dir)
717 patches.append(os.path.basename(patch_path), "")
719 product_pyconf_cfg[p_info.section].patches = patches
722 # put in the pyconf file the resolved values
723 for info in ["git_info", "cvs_info", "svn_info"]:
725 for key in p_info[info]:
726 product_pyconf_cfg[p_info.section][info][key] = p_info[
729 # if the product is not archive, then make it become archive.
730 if src.product.product_is_vcs(p_info):
731 product_pyconf_cfg[p_info.section].get_source = "archive"
732 if not "archive_info" in product_pyconf_cfg[p_info.section]:
733 product_pyconf_cfg[p_info.section].addMapping("archive_info",
734 src.pyconf.Mapping(product_pyconf_cfg),
736 product_pyconf_cfg[p_info.section
737 ].archive_info.archive_name = p_info.name + ".tgz"
739 # write the pyconf file to the temporary project location
740 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
742 ff = open(product_tmp_pyconf_path, 'w')
743 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
744 product_pyconf_cfg.__save__(ff, 1)
747 def find_application_pyconf(config, application_tmp_dir):
748 '''Find the application pyconf file and put it in the specific temporary
749 directory containing the specific project of a source package.
751 :param config Config: The global configuration.
752 :param application_tmp_dir str: The path to the temporary application
753 scripts directory of the project.
755 # read the pyconf of the application
756 application_name = config.VARS.application
757 application_pyconf_path = src.find_file_in_lpath(
758 application_name + ".pyconf",
759 config.PATHS.APPLICATIONPATH)
760 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
763 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
764 application_pyconf_cfg,
766 'VARS.salometoolsway + $VARS.sep + ".."')
768 # Prevent from compilation in base
769 application_pyconf_cfg.APPLICATION.no_base = "yes"
771 # write the pyconf file to the temporary application location
772 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
773 application_name + ".pyconf")
774 ff = open(application_tmp_pyconf_path, 'w')
775 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
776 application_pyconf_cfg.__save__(ff, 1)
779 def project_package(project_file_path, tmp_working_dir):
780 '''Prepare a dictionary that stores all the needed directories and files to
781 add in a project package.
783 :param project_file_path str: The path to the local project.
784 :param tmp_working_dir str: The temporary local directory containing some
785 specific directories or files needed in the
787 :return: the dictionary that stores all the needed directories and files to
788 add in a project package.
789 {label : (path_on_local_machine, path_in_archive)}
793 # Read the project file and get the directories to add to the package
794 project_pyconf_cfg = src.pyconf.Config(project_file_path)
795 paths = {"ARCHIVEPATH" : "archives",
796 "APPLICATIONPATH" : "applications",
797 "PRODUCTPATH" : "products",
799 "MACHINEPATH" : "machines"}
800 # Loop over the project paths and add it
802 if path not in project_pyconf_cfg:
804 # Add the directory to the files to add in the package
805 d_project[path] = (project_pyconf_cfg[path], paths[path])
806 # Modify the value of the path in the package
807 project_pyconf_cfg[path] = src.pyconf.Reference(
810 'project_path + "/' + paths[path] + '"')
813 if "project_path" not in project_pyconf_cfg:
814 project_pyconf_cfg.addMapping("project_path",
815 src.pyconf.Mapping(project_pyconf_cfg),
817 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
821 # Write the project pyconf file
822 project_file_name = os.path.basename(project_file_path)
823 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
824 ff = open(project_pyconf_tmp_path, 'w')
825 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
826 project_pyconf_cfg.__save__(ff, 1)
828 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
832 def add_readme(config, package_type, where):
833 readme_path = os.path.join(where, "README")
834 f = open(readme_path, 'w')
835 # prepare substitution dictionary
837 if package_type == BINARY:
838 d['application'] = config.VARS.application
839 d['user'] = config.VARS.user
840 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
841 d['version'] = config.INTERNAL.sat_version
842 d['dist'] = config.VARS.dist
843 if 'profile' in config.APPLICATION:
844 d['launcher'] = config.APPLICATION.profile.launcher_name
845 readme_template_path = os.path.join(config.VARS.internal_dir,
846 "README_BIN.template")
848 d['env_file'] = 'env_launch.sh'
849 readme_template_path = os.path.join(config.VARS.internal_dir,
850 "README_BIN_NO_PROFILE.template")
852 if package_type == SOURCE:
853 d['application'] = config.VARS.application
854 d['user'] = config.VARS.user
855 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
856 d['version'] = config.INTERNAL.sat_version
857 if 'profile' in config.APPLICATION:
858 d['profile'] = config.APPLICATION.profile.product
859 d['launcher'] = config.APPLICATION.profile.launcher_name
860 readme_template_path = os.path.join(config.VARS.internal_dir,
861 "README_SRC.template")
863 if package_type == PROJECT:
864 d['user'] = config.VARS.user
865 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
866 d['version'] = config.INTERNAL.sat_version
867 readme_template_path = os.path.join(config.VARS.internal_dir,
868 "README_PROJECT.template")
870 if package_type == SAT:
871 d['user'] = config.VARS.user
872 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
873 d['version'] = config.INTERNAL.sat_version
874 readme_template_path = os.path.join(config.VARS.internal_dir,
875 "README_SAT.template")
877 f.write(src.template.substitute(readme_template_path, d))
883 '''method that is called when salomeTools is called with --help option.
885 :return: The text to display for the package command description.
888 return _("The package command creates an archive.\nThere are 4 kinds of "
889 "archive:\n 1- The binary archive. It contains all the product "
890 "installation directories and a launcher,\n 2- The sources archive."
891 " It contains the products archives, a project corresponding to "
892 "the application and salomeTools,\n 3- The project archive. It "
893 "contains a project (give the project file path as argument),\n 4-"
894 " The salomeTools archive. It contains salomeTools.\n\nexample:"
895 "\nsat package SALOME-master --sources")
897 def run(args, runner, logger):
898 '''method that is called when salomeTools is called with package parameter.
902 (options, args) = parser.parse_args(args)
904 # Check that a type of package is called, and only one
905 all_option_types = (options.binaries,
907 options.project not in ["", None],
910 # Check if no option for package type
911 if all_option_types.count(True) == 0:
912 msg = _("Error: Precise a type for the package\nUse one of the "
913 "following options: --binaries, --sources, --project or"
915 logger.write(src.printcolors.printcError(msg), 1)
916 logger.write("\n", 1)
919 # Check for only one option for package type
920 if all_option_types.count(True) > 1:
921 msg = _("Error: You can use only one type for the package\nUse only one"
922 " of the following options: --binaries, --sources, --project or"
924 logger.write(src.printcolors.printcError(msg), 1)
925 logger.write("\n", 1)
928 # Get the package type
930 package_type = BINARY
932 package_type = SOURCE
934 package_type = PROJECT
938 # The repository where to put the package if not Binary or Source
939 package_default_path = runner.cfg.USER.workdir
941 if package_type in [BINARY, SOURCE]:
942 # Check that the command has been called with an application
943 src.check_config_has_application(runner.cfg)
945 # Display information
946 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
947 runner.cfg.VARS.application), 1)
949 # Get the default directory where to put the packages
950 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
952 src.ensure_path_exists(package_default_path)
954 elif package_type == PROJECT:
955 # check that the project is visible by SAT
956 if options.project not in runner.cfg.PROJECTS.project_file_paths:
957 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
960 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
961 "\nPlease add it in the %(site)s file." % {
962 "proj" : options.project, "site" : site_path})
963 logger.write(src.printcolors.printcError(msg), 1)
964 logger.write("\n", 1)
968 src.printcolors.print_value(logger, "Package type", package_type, 2)
970 # get the name of the archive or construct it
972 if os.path.basename(options.name) == options.name:
973 # only a name (not a path)
974 archive_name = options.name
975 dir_name = package_default_path
977 archive_name = os.path.basename(options.name)
978 dir_name = os.path.dirname(options.name)
981 if archive_name[-len(".tgz"):] == ".tgz":
982 archive_name = archive_name[:-len(".tgz")]
983 if archive_name[-len(".tar.gz"):] == ".tar.gz":
984 archive_name = archive_name[:-len(".tar.gz")]
987 dir_name = package_default_path
988 if package_type == BINARY:
989 archive_name = (runner.cfg.APPLICATION.name +
991 runner.cfg.VARS.dist)
993 if package_type == SOURCE:
994 archive_name = (runner.cfg.APPLICATION.name +
998 archive_name = (runner.cfg.APPLICATION.name +
1004 if package_type == PROJECT:
1005 project_name, __ = os.path.splitext(
1006 os.path.basename(options.project))
1007 archive_name = ("PROJECT" +
1011 if package_type == SAT:
1012 archive_name = ("salomeTools" +
1014 runner.cfg.INTERNAL.sat_version)
1016 path_targz = os.path.join(dir_name, archive_name + ".tgz")
1018 # Print the path of the package
1019 src.printcolors.print_value(logger, "Package path", path_targz, 2)
1021 # Create a working directory for all files that are produced during the
1022 # package creation and that will be removed at the end of the command
1023 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
1024 runner.cfg.VARS.datehour)
1025 src.ensure_path_exists(tmp_working_dir)
1026 logger.write("\n", 5)
1027 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
1029 logger.write("\n", 3)
1031 msg = _("Preparation of files to add to the archive")
1032 logger.write(src.printcolors.printcLabel(msg), 2)
1033 logger.write("\n", 2)
1035 if package_type == BINARY:
1036 d_files_to_add = binary_package(runner.cfg,
1040 if not(d_files_to_add):
1043 if package_type == SOURCE:
1044 d_files_to_add = source_package(runner,
1050 if package_type == PROJECT:
1051 d_files_to_add = project_package(options.project, tmp_working_dir)
1053 if package_type == SAT:
1054 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
1056 # Add the README file in the package
1057 local_readme_tmp_path = add_readme(runner.cfg,
1060 d_files_to_add["README"] = (local_readme_tmp_path, "README")
1062 # Add the additional files of option add_files
1063 if options.add_files:
1064 for file_path in options.add_files:
1065 if not os.path.exists(file_path):
1066 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1068 file_name = os.path.basename(file_path)
1069 d_files_to_add[file_name] = (file_path, file_name)
1071 logger.write("\n", 2)
1073 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1074 logger.write("\n", 2)
1077 # Creating the object tarfile
1078 tar = tarfile.open(path_targz, mode='w:gz')
1080 # Add the files to the tarfile object
1081 res = add_files(tar, archive_name, d_files_to_add, logger)
1083 except KeyboardInterrupt:
1084 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1085 logger.write(_("Removing the temporary working directory ... "), 1)
1086 # remove the working directory
1087 shutil.rmtree(tmp_working_dir)
1088 logger.write(_("OK"), 1)
1089 logger.write(_("\n"), 1)
1092 # remove the working directory
1093 shutil.rmtree(tmp_working_dir)
1095 # Print again the path of the package
1096 logger.write("\n", 2)
1097 src.printcolors.print_value(logger, "Package path", path_targz, 2)