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 IGNORED_DIRS = [".git"]
38 IGNORED_EXTENSIONS = [".la", ".cmake"]
40 PROJECT_TEMPLATE = """#!/usr/bin/env python
43 # The path to the archive root directory
44 root_path : $PWD + "/../"
46 project_path : $PWD + "/"
48 # Where to search the archives of the products
49 ARCHIVEPATH : $root_path + "ARCHIVES"
50 # Where to search the pyconf of the applications
51 APPLICATIONPATH : $project_path + "applications/"
52 # Where to search the pyconf of the products
53 PRODUCTPATH : $project_path + "products/"
54 # Where to search the pyconf of the jobs of the project
55 JOBPATH : $project_path + "jobs/"
56 # Where to search the pyconf of the machines of the project
57 MACHINEPATH : $project_path + "machines/"
60 SITE_TEMPLATE = ("""#!/usr/bin/env python
67 log_dir : $USER.workdir + "/LOGS"
70 tmp_dir_with_application : '/tmp' + $VARS.sep + $VARS.user + """
71 """$VARS.sep + $APPLICATION.name + $VARS.sep + 'test'
72 tmp_dir : '/tmp' + $VARS.sep + $VARS.user + $VARS.sep + 'test'
79 project_file_paths : [$VARS.salometoolsway + $VARS.sep + \"..\" + $VARS.sep"""
80 """ + \"""" + PROJECT_DIR + """\" + $VARS.sep + "project.pyconf"]
84 # Define all possible option for the package command : sat package <options>
85 parser = src.options.Options()
86 parser.add_option('b', 'binaries', 'boolean', 'binaries',
87 _('Optional: Produce a binary package.'), False)
88 parser.add_option('f', 'force_creation', 'boolean', 'force_creation',
89 _('Optional: Only binary package: produce the archive even if '
90 'there are some missing products.'), False)
91 parser.add_option('s', 'sources', 'boolean', 'sources',
92 _('Optional: Produce a compilable archive of the sources of the '
93 'application.'), False)
94 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
95 _('Optional: Only source package: do not make archive of vcs products.'),
97 parser.add_option('p', 'project', 'string', 'project',
98 _('Optional: Produce an archive that contains a project.'), "")
99 parser.add_option('t', 'salometools', 'boolean', 'sat',
100 _('Optional: Produce an archive that contains salomeTools.'), False)
101 parser.add_option('n', 'name', 'string', 'name',
102 _('Optional: The name or full path of the archive.'), None)
103 parser.add_option('', 'add_files', 'list2', 'add_files',
104 _('Optional: The list of additional files to add to the archive.'), [])
105 parser.add_option('', 'without_commercial', 'boolean', 'without_commercial',
106 _('Optional: do not add commercial licence.'), False)
107 parser.add_option('', 'without_property', 'string', 'without_property',
108 _('Optional: Filter the products by their properties.\n\tSyntax: '
109 '--without_property <property>:<value>'))
112 def add_files(tar, name_archive, d_content, logger, f_exclude=None):
113 '''Create an archive containing all directories and files that are given in
114 the d_content argument.
116 :param tar tarfile: The tarfile instance used to make the archive.
117 :param name_archive str: The name of the archive to make.
118 :param d_content dict: The dictionary that contain all directories and files
119 to add in the archive.
121 (path_on_local_machine, path_in_archive)
122 :param logger Logger: the logging instance
123 :param f_exclude Function: the function that filters
124 :return: 0 if success, 1 if not.
127 # get the max length of the messages in order to make the display
128 max_len = len(max(d_content.keys(), key=len))
131 # loop over each directory or file stored in the d_content dictionary
132 for name in d_content.keys():
133 # display information
134 len_points = max_len - len(name)
135 logger.write(name + " " + len_points * "." + " ", 3)
136 # Get the local path and the path in archive
137 # of the directory or file to add
138 local_path, archive_path = d_content[name]
139 in_archive = os.path.join(name_archive, archive_path)
140 # Add it in the archive
142 tar.add(local_path, arcname=in_archive, exclude=f_exclude)
143 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
144 except Exception as e:
145 logger.write(src.printcolors.printcError(_("KO ")), 3)
146 logger.write(str(e), 3)
148 logger.write("\n", 3)
151 def exclude_VCS_info(filename):
152 ''' The function that is used to exclude from package the link to the
153 VCS repositories (like .git)
155 :param filename Str: The filname to exclude (or not).
156 :return: True if the file has to be exclude
159 for dir_name in IGNORED_DIRS:
160 if dir_name in filename:
162 for extension in IGNORED_EXTENSIONS:
163 if filename.endswith(extension):
167 def produce_relative_launcher(config,
172 with_commercial=True):
173 '''Create a specific SALOME launcher for the binary package. This launcher
176 :param config Config: The global configuration.
177 :param logger Logger: the logging instance
178 :param file_dir str: the directory where to put the launcher
179 :param file_name str: The launcher name
180 :param binaries_dir_name str: the name of the repository where the binaries
182 :return: the path of the produced launcher
186 # Get the launcher template
187 profile_install_dir = os.path.join(binaries_dir_name,
188 config.APPLICATION.profile.product)
189 withProfile = src.fileEnviron.withProfile
190 withProfile = withProfile.replace(
191 "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
192 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
193 withProfile = withProfile.replace(
194 "os.path.join( 'PROFILE_INSTALL_DIR'",
195 "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
197 before, after = withProfile.split(
198 "# here your local standalone environment\n")
200 # create an environment file writer
201 writer = src.environment.FileEnvWriter(config,
206 filepath = os.path.join(file_dir, file_name)
207 # open the file and write into it
208 launch_file = open(filepath, "w")
209 launch_file.write(before)
211 writer.write_cfgForPy_file(launch_file,
212 for_package = binaries_dir_name,
213 with_commercial=with_commercial)
214 launch_file.write(after)
217 # Little hack to put out_dir_Path outside the strings
218 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
220 # change the rights in order to make the file executable for everybody
232 def produce_relative_env_files(config,
236 '''Create some specific environment files for the binary package. These
237 files use relative paths.
239 :param config Config: The global configuration.
240 :param logger Logger: the logging instance
241 :param file_dir str: the directory where to put the files
242 :param binaries_dir_name str: the name of the repository where the binaries
244 :return: the list of path of the produced environment files
247 # create an environment file writer
248 writer = src.environment.FileEnvWriter(config,
254 filepath = writer.write_env_file("env_launch.sh",
257 for_package = binaries_dir_name)
259 # Little hack to put out_dir_Path as environment variable
260 src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
262 # change the rights in order to make the file executable for everybody
274 def product_appli_creation_script(config,
278 '''Create a script that can produce an application (EDF style) in the binary
281 :param config Config: The global configuration.
282 :param logger Logger: the logging instance
283 :param file_dir str: the directory where to put the file
284 :param binaries_dir_name str: the name of the repository where the binaries
286 :return: the path of the produced script file
289 template_name = "create_appli.py.for_bin_packages.template"
290 template_path = os.path.join(config.VARS.internal_dir, template_name)
291 text_to_fill = open(template_path, "r").read()
292 text_to_fill = text_to_fill.replace("TO BE FILLED 1",
293 '"' + binaries_dir_name + '"')
296 for product_name in get_SALOME_modules(config):
297 product_info = src.product.get_product_config(config, product_name)
299 if src.product.product_is_smesh_plugin(product_info):
302 if 'install_dir' in product_info and bool(product_info.install_dir):
303 if src.product.product_is_cpp(product_info):
305 for cpp_name in src.product.get_product_components(product_info):
306 line_to_add = ("<module name=\"" +
308 "\" gui=\"yes\" path=\"''' + "
309 "os.path.join(dir_bin_name, \"" +
310 cpp_name + "\") + '''\"/>")
313 line_to_add = ("<module name=\"" +
315 "\" gui=\"yes\" path=\"''' + "
316 "os.path.join(dir_bin_name, \"" +
317 product_name + "\") + '''\"/>")
318 text_to_add += line_to_add + "\n"
320 filled_text = text_to_fill.replace("TO BE FILLED 2", text_to_add)
322 tmp_file_path = os.path.join(file_dir, "create_appli.py")
323 ff = open(tmp_file_path, "w")
324 ff.write(filled_text)
327 # change the rights in order to make the file executable for everybody
328 os.chmod(tmp_file_path,
339 def binary_package(config, logger, options, tmp_working_dir):
340 '''Prepare a dictionary that stores all the needed directories and files to
341 add in a binary package.
343 :param config Config: The global configuration.
344 :param logger Logger: the logging instance
345 :param options OptResult: the options of the launched command
346 :param tmp_working_dir str: The temporary local directory containing some
347 specific directories or files needed in the
349 :return: the dictionary that stores all the needed directories and files to
350 add in a binary package.
351 {label : (path_on_local_machine, path_in_archive)}
355 # Get the list of product installation to add to the archive
356 l_products_name = config.APPLICATION.products.keys()
357 l_product_info = src.product.get_products_infos(l_products_name,
362 l_sources_not_present = []
363 for prod_name, prod_info in l_product_info:
365 # Add the sources of the products that have the property
366 # sources_in_package : "yes"
367 if src.get_property_in_product_cfg(prod_info,
368 "sources_in_package") == "yes":
369 if os.path.exists(prod_info.source_dir):
370 l_source_dir.append((prod_name, prod_info.source_dir))
372 l_sources_not_present.append(prod_name)
374 # ignore the native and fixed products for install directories
375 if (src.product.product_is_native(prod_info)
376 or src.product.product_is_fixed(prod_info)
377 or not src.product.product_compiles(prod_info)):
379 if src.product.check_installation(prod_info):
380 l_install_dir.append((prod_name, prod_info.install_dir))
382 l_not_installed.append(prod_name)
384 # Add also the cpp generated modules (if any)
385 if src.product.product_is_cpp(prod_info):
387 for name_cpp in src.product.get_product_components(prod_info):
388 install_dir = os.path.join(config.APPLICATION.workdir,
390 if os.path.exists(install_dir):
391 l_install_dir.append((name_cpp, install_dir))
393 l_not_installed.append(name_cpp)
395 # Print warning or error if there are some missing products
396 if len(l_not_installed) > 0:
397 text_missing_prods = ""
398 for p_name in l_not_installed:
399 text_missing_prods += "-" + p_name + "\n"
400 if not options.force_creation:
401 msg = _("ERROR: there are missing products installations:")
402 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
407 msg = _("WARNING: there are missing products installations:")
408 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
412 # Do the same for sources
413 if len(l_sources_not_present) > 0:
414 text_missing_prods = ""
415 for p_name in l_sources_not_present:
416 text_missing_prods += "-" + p_name + "\n"
417 if not options.force_creation:
418 msg = _("ERROR: there are missing products sources:")
419 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
424 msg = _("WARNING: there are missing products sources:")
425 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
429 # construct the name of the directory that will contain the binaries
430 binaries_dir_name = "BINARIES-" + config.VARS.dist
432 # construct the correlation table between the product names, there
433 # actual install directories and there install directory in archive
435 for prod_name, install_dir in l_install_dir:
436 path_in_archive = os.path.join(binaries_dir_name, prod_name)
437 d_products[prod_name] = (install_dir, path_in_archive)
439 for prod_name, source_dir in l_source_dir:
440 path_in_archive = os.path.join("SOURCES", prod_name)
441 d_products[prod_name + " (sources)"] = (source_dir, path_in_archive)
443 # create the relative launcher and add it to the files to add
444 if ("profile" in config.APPLICATION and
445 "product" in config.APPLICATION.profile):
446 launcher_name = config.APPLICATION.profile.launcher_name
447 launcher_package = produce_relative_launcher(config,
452 not(options.without_commercial))
454 d_products["launcher"] = (launcher_package, launcher_name)
456 # Provide a script for the creation of an application EDF style
457 appli_script = product_appli_creation_script(config,
462 d_products["appli script"] = (appli_script, "create_appli.py")
464 # Put also the environment file
465 env_file = produce_relative_env_files(config,
470 d_products["environment file"] = (env_file, "env_launch.sh")
474 def source_package(sat, config, logger, options, tmp_working_dir):
475 '''Prepare a dictionary that stores all the needed directories and files to
476 add in a source package.
478 :param config Config: The global configuration.
479 :param logger Logger: the logging instance
480 :param options OptResult: the options of the launched command
481 :param tmp_working_dir str: The temporary local directory containing some
482 specific directories or files needed in the
484 :return: the dictionary that stores all the needed directories and files to
485 add in a source package.
486 {label : (path_on_local_machine, path_in_archive)}
490 # Get all the products that are prepared using an archive
491 logger.write("Find archive products ... ")
492 d_archives, l_pinfo_vcs = get_archives(config, logger)
493 logger.write("Done\n")
495 if not options.with_vcs and len(l_pinfo_vcs) > 0:
496 # Make archives with the products that are not prepared using an archive
497 # (git, cvs, svn, etc)
498 logger.write("Construct archives for vcs products ... ")
499 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
504 logger.write("Done\n")
507 logger.write("Create the project ... ")
508 d_project = create_project_for_src_package(config,
511 logger.write("Done\n")
514 tmp_sat = add_salomeTools(config, tmp_working_dir)
515 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
517 # Add a sat symbolic link if not win
518 if not src.architecture.is_windows():
519 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
523 # In the jobs, os.getcwd() can fail
524 t = config.USER.workdir
525 os.chdir(tmp_working_dir)
526 if os.path.lexists(tmp_satlink_path):
527 os.remove(tmp_satlink_path)
528 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
531 d_sat["sat link"] = (tmp_satlink_path, "sat")
533 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
535 def get_archives(config, logger):
536 '''Find all the products that are get using an archive and all the products
537 that are get using a vcs (git, cvs, svn) repository.
539 :param config Config: The global configuration.
540 :param logger Logger: the logging instance
541 :return: the dictionary {name_product :
542 (local path of its archive, path in the package of its archive )}
543 and the list of specific configuration corresponding to the vcs
547 # Get the list of product informations
548 l_products_name = config.APPLICATION.products.keys()
549 l_product_info = src.product.get_products_infos(l_products_name,
553 for p_name, p_info in l_product_info:
554 # ignore the native and fixed products
555 if (src.product.product_is_native(p_info)
556 or src.product.product_is_fixed(p_info)):
558 if p_info.get_source == "archive":
559 archive_path = p_info.archive_info.archive_name
560 archive_name = os.path.basename(archive_path)
562 l_pinfo_vcs.append((p_name, p_info))
564 d_archives[p_name] = (archive_path,
565 os.path.join(ARCHIVE_DIR, archive_name))
566 return d_archives, l_pinfo_vcs
568 def add_salomeTools(config, tmp_working_dir):
569 '''Prepare a version of salomeTools that has a specific site.pyconf file
570 configured for a source package.
572 :param config Config: The global configuration.
573 :param tmp_working_dir str: The temporary local directory containing some
574 specific directories or files needed in the
576 :return: The path to the local salomeTools directory to add in the package
579 # Copy sat in the temporary working directory
580 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
581 sat_running_path = src.Path(config.VARS.salometoolsway)
582 sat_running_path.copy(sat_tmp_path)
584 # Update the site.pyconf file that contains the path to the project
585 site_pyconf_name = "site.pyconf"
586 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
587 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
588 ff = open(site_pyconf_file, "w")
589 ff.write(SITE_TEMPLATE)
592 return sat_tmp_path.path
594 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
595 '''For sources package that require that all products are get using an
596 archive, one has to create some archive for the vcs products.
597 So this method calls the clean and source command of sat and then create
600 :param l_pinfo_vcs List: The list of specific configuration corresponding to
602 :param sat Sat: The Sat instance that can be called to clean and source the
604 :param config Config: The global configuration.
605 :param logger Logger: the logging instance
606 :param tmp_working_dir str: The temporary local directory containing some
607 specific directories or files needed in the
609 :return: the dictionary that stores all the archives to add in the source
610 package. {label : (path_on_local_machine, path_in_archive)}
613 # clean the source directory of all the vcs products, then use the source
614 # command and thus construct an archive that will not contain the patches
615 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
617 logger.write(_("clean sources\n"))
618 args_clean = config.VARS.application
619 args_clean += " --sources --products "
620 args_clean += ",".join(l_prod_names)
621 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
623 logger.write(_("get sources"))
624 args_source = config.VARS.application
625 args_source += " --products "
626 args_source += ",".join(l_prod_names)
627 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
629 # make the new archives
631 for pn, pinfo in l_pinfo_vcs:
632 path_archive = make_archive(pn, pinfo, tmp_working_dir)
633 d_archives_vcs[pn] = (path_archive,
634 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
635 return d_archives_vcs
637 def make_archive(prod_name, prod_info, where):
638 '''Create an archive of a product by searching its source directory.
640 :param prod_name str: The name of the product.
641 :param prod_info Config: The specific configuration corresponding to the
643 :param where str: The path of the repository where to put the resulting
645 :return: The path of the resulting archive
648 path_targz_prod = os.path.join(where, prod_name + ".tgz")
649 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
650 local_path = prod_info.source_dir
651 tar_prod.add(local_path, arcname=prod_name)
653 return path_targz_prod
655 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
656 '''Create a specific project for a source package.
658 :param config Config: The global configuration.
659 :param tmp_working_dir str: The temporary local directory containing some
660 specific directories or files needed in the
662 :param with_vcs boolean: True if the package is with vcs products (not
663 transformed into archive products)
664 :return: The dictionary
665 {"project" : (produced project, project path in the archive)}
669 # Create in the working temporary directory the full project tree
670 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
671 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
673 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
676 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
679 patches_tmp_dir = os.path.join(project_tmp_dir,
682 application_tmp_dir = os.path.join(project_tmp_dir,
684 for directory in [project_tmp_dir,
685 compil_scripts_tmp_dir,
688 application_tmp_dir]:
689 src.ensure_path_exists(directory)
691 # Create the pyconf that contains the information of the project
692 project_pyconf_name = "project.pyconf"
693 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
694 ff = open(project_pyconf_file, "w")
695 ff.write(PROJECT_TEMPLATE)
698 # Loop over the products to get there pyconf and all the scripts
699 # (compilation, environment, patches)
700 # and create the pyconf file to add to the project
701 lproducts_name = config.APPLICATION.products.keys()
702 l_products = src.product.get_products_infos(lproducts_name, config)
703 for p_name, p_info in l_products:
704 # ignore native and fixed products
705 if (src.product.product_is_native(p_info) or
706 src.product.product_is_fixed(p_info)):
708 find_product_scripts_and_pyconf(p_name,
712 compil_scripts_tmp_dir,
715 products_pyconf_tmp_dir)
717 find_application_pyconf(config, application_tmp_dir)
719 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
722 def find_product_scripts_and_pyconf(p_name,
726 compil_scripts_tmp_dir,
729 products_pyconf_tmp_dir):
730 '''Create a specific pyconf file for a given product. Get its environment
731 script, its compilation script and patches and put it in the temporary
732 working directory. This method is used in the source package in order to
733 construct the specific project.
735 :param p_name str: The name of the product.
736 :param p_info Config: The specific configuration corresponding to the
738 :param config Config: The global configuration.
739 :param with_vcs boolean: True if the package is with vcs products (not
740 transformed into archive products)
741 :param compil_scripts_tmp_dir str: The path to the temporary compilation
742 scripts directory of the project.
743 :param env_scripts_tmp_dir str: The path to the temporary environment script
744 directory of the project.
745 :param patches_tmp_dir str: The path to the temporary patch scripts
746 directory of the project.
747 :param products_pyconf_tmp_dir str: The path to the temporary product
748 scripts directory of the project.
751 # read the pyconf of the product
752 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
753 config.PATHS.PRODUCTPATH)
754 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
756 # find the compilation script if any
757 if src.product.product_has_script(p_info):
758 compil_script_path = src.Path(p_info.compil_script)
759 compil_script_path.copy(compil_scripts_tmp_dir)
760 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
761 p_info.compil_script)
762 # find the environment script if any
763 if src.product.product_has_env_script(p_info):
764 env_script_path = src.Path(p_info.environ.env_script)
765 env_script_path.copy(env_scripts_tmp_dir)
766 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
767 p_info.environ.env_script)
768 # find the patches if any
769 if src.product.product_has_patches(p_info):
770 patches = src.pyconf.Sequence()
771 for patch_path in p_info.patches:
772 p_path = src.Path(patch_path)
773 p_path.copy(patches_tmp_dir)
774 patches.append(os.path.basename(patch_path), "")
776 product_pyconf_cfg[p_info.section].patches = patches
779 # put in the pyconf file the resolved values
780 for info in ["git_info", "cvs_info", "svn_info"]:
782 for key in p_info[info]:
783 product_pyconf_cfg[p_info.section][info][key] = p_info[
786 # if the product is not archive, then make it become archive.
787 if src.product.product_is_vcs(p_info):
788 product_pyconf_cfg[p_info.section].get_source = "archive"
789 if not "archive_info" in product_pyconf_cfg[p_info.section]:
790 product_pyconf_cfg[p_info.section].addMapping("archive_info",
791 src.pyconf.Mapping(product_pyconf_cfg),
793 product_pyconf_cfg[p_info.section
794 ].archive_info.archive_name = p_info.name + ".tgz"
796 # write the pyconf file to the temporary project location
797 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
799 ff = open(product_tmp_pyconf_path, 'w')
800 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
801 product_pyconf_cfg.__save__(ff, 1)
804 def find_application_pyconf(config, application_tmp_dir):
805 '''Find the application pyconf file and put it in the specific temporary
806 directory containing the specific project of a source package.
808 :param config Config: The global configuration.
809 :param application_tmp_dir str: The path to the temporary application
810 scripts directory of the project.
812 # read the pyconf of the application
813 application_name = config.VARS.application
814 application_pyconf_path = src.find_file_in_lpath(
815 application_name + ".pyconf",
816 config.PATHS.APPLICATIONPATH)
817 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
820 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
821 application_pyconf_cfg,
823 'VARS.salometoolsway + $VARS.sep + ".."')
825 # Prevent from compilation in base
826 application_pyconf_cfg.APPLICATION.no_base = "yes"
828 # write the pyconf file to the temporary application location
829 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
830 application_name + ".pyconf")
831 ff = open(application_tmp_pyconf_path, 'w')
832 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
833 application_pyconf_cfg.__save__(ff, 1)
836 def project_package(project_file_path, tmp_working_dir):
837 '''Prepare a dictionary that stores all the needed directories and files to
838 add in a project package.
840 :param project_file_path str: The path to the local project.
841 :param tmp_working_dir str: The temporary local directory containing some
842 specific directories or files needed in the
844 :return: the dictionary that stores all the needed directories and files to
845 add in a project package.
846 {label : (path_on_local_machine, path_in_archive)}
850 # Read the project file and get the directories to add to the package
851 project_pyconf_cfg = src.pyconf.Config(project_file_path)
852 paths = {"ARCHIVEPATH" : "archives",
853 "APPLICATIONPATH" : "applications",
854 "PRODUCTPATH" : "products",
856 "MACHINEPATH" : "machines"}
857 # Loop over the project paths and add it
859 if path not in project_pyconf_cfg:
861 # Add the directory to the files to add in the package
862 d_project[path] = (project_pyconf_cfg[path], paths[path])
863 # Modify the value of the path in the package
864 project_pyconf_cfg[path] = src.pyconf.Reference(
867 'project_path + "/' + paths[path] + '"')
870 if "project_path" not in project_pyconf_cfg:
871 project_pyconf_cfg.addMapping("project_path",
872 src.pyconf.Mapping(project_pyconf_cfg),
874 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
878 # Write the project pyconf file
879 project_file_name = os.path.basename(project_file_path)
880 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
881 ff = open(project_pyconf_tmp_path, 'w')
882 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
883 project_pyconf_cfg.__save__(ff, 1)
885 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
889 def add_readme(config, package_type, where):
890 readme_path = os.path.join(where, "README")
891 f = open(readme_path, 'w')
892 # prepare substitution dictionary
894 if package_type == BINARY:
895 d['application'] = config.VARS.application
896 d['user'] = config.VARS.user
897 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
898 d['version'] = config.INTERNAL.sat_version
899 d['dist'] = config.VARS.dist
900 if 'profile' in config.APPLICATION:
901 d['launcher'] = config.APPLICATION.profile.launcher_name
902 readme_template_path = os.path.join(config.VARS.internal_dir,
903 "README_BIN.template")
905 d['env_file'] = 'env_launch.sh'
906 readme_template_path = os.path.join(config.VARS.internal_dir,
907 "README_BIN_NO_PROFILE.template")
909 if package_type == SOURCE:
910 d['application'] = config.VARS.application
911 d['user'] = config.VARS.user
912 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
913 d['version'] = config.INTERNAL.sat_version
914 if 'profile' in config.APPLICATION:
915 d['profile'] = config.APPLICATION.profile.product
916 d['launcher'] = config.APPLICATION.profile.launcher_name
917 readme_template_path = os.path.join(config.VARS.internal_dir,
918 "README_SRC.template")
920 if package_type == PROJECT:
921 d['user'] = config.VARS.user
922 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
923 d['version'] = config.INTERNAL.sat_version
924 readme_template_path = os.path.join(config.VARS.internal_dir,
925 "README_PROJECT.template")
927 if package_type == SAT:
928 d['user'] = config.VARS.user
929 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
930 d['version'] = config.INTERNAL.sat_version
931 readme_template_path = os.path.join(config.VARS.internal_dir,
932 "README_SAT.template")
934 f.write(src.template.substitute(readme_template_path, d))
938 def update_config(config, prop, value):
939 '''Remove from config.APPLICATION.products the products that have the property given as input.
941 :param config Config: The global config.
942 :param prop str: The property to filter
943 :param value str: The value of the property to filter
945 src.check_config_has_application(config)
946 l_product_to_remove = []
947 for product_name in config.APPLICATION.products.keys():
948 prod_cfg = src.product.get_product_config(config, product_name)
949 if src.get_property_in_product_cfg(prod_cfg, prop) == value:
950 l_product_to_remove.append(product_name)
951 for product_name in l_product_to_remove:
952 config.APPLICATION.products.__delitem__(product_name)
955 '''method that is called when salomeTools is called with --help option.
957 :return: The text to display for the package command description.
960 return _("The package command creates an archive.\nThere are 4 kinds of "
961 "archive:\n 1- The binary archive. It contains all the product "
962 "installation directories and a launcher,\n 2- The sources archive."
963 " It contains the products archives, a project corresponding to "
964 "the application and salomeTools,\n 3- The project archive. It "
965 "contains a project (give the project file path as argument),\n 4-"
966 " The salomeTools archive. It contains salomeTools.\n\nexample:"
967 "\nsat package SALOME-master --sources")
969 def run(args, runner, logger):
970 '''method that is called when salomeTools is called with package parameter.
974 (options, args) = parser.parse_args(args)
976 # Check that a type of package is called, and only one
977 all_option_types = (options.binaries,
979 options.project not in ["", None],
982 # Check if no option for package type
983 if all_option_types.count(True) == 0:
984 msg = _("Error: Precise a type for the package\nUse one of the "
985 "following options: --binaries, --sources, --project or"
987 logger.write(src.printcolors.printcError(msg), 1)
988 logger.write("\n", 1)
991 # Check for only one option for package type
992 if all_option_types.count(True) > 1:
993 msg = _("Error: You can use only one type for the package\nUse only one"
994 " of the following options: --binaries, --sources, --project or"
996 logger.write(src.printcolors.printcError(msg), 1)
997 logger.write("\n", 1)
1000 # Get the package type
1001 if options.binaries:
1002 package_type = BINARY
1004 package_type = SOURCE
1006 package_type = PROJECT
1010 # The repository where to put the package if not Binary or Source
1011 package_default_path = runner.cfg.USER.workdir
1013 if package_type in [BINARY, SOURCE]:
1014 # Check that the command has been called with an application
1015 src.check_config_has_application(runner.cfg)
1017 # Display information
1018 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
1019 runner.cfg.VARS.application), 1)
1021 # Get the default directory where to put the packages
1022 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
1024 src.ensure_path_exists(package_default_path)
1026 elif package_type == PROJECT:
1027 # check that the project is visible by SAT
1028 if options.project not in runner.cfg.PROJECTS.project_file_paths:
1029 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
1032 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
1033 "\nPlease add it in the %(site)s file." % {
1034 "proj" : options.project, "site" : site_path})
1035 logger.write(src.printcolors.printcError(msg), 1)
1036 logger.write("\n", 1)
1039 # Remove the products that are filtered by the --without_property option
1040 if options.without_property:
1041 [prop, value] = options.without_property.split(":")
1042 update_config(runner.cfg, prop, value)
1045 src.printcolors.print_value(logger, "Package type", package_type, 2)
1047 # get the name of the archive or construct it
1049 if os.path.basename(options.name) == options.name:
1050 # only a name (not a path)
1051 archive_name = options.name
1052 dir_name = package_default_path
1054 archive_name = os.path.basename(options.name)
1055 dir_name = os.path.dirname(options.name)
1057 # suppress extension
1058 if archive_name[-len(".tgz"):] == ".tgz":
1059 archive_name = archive_name[:-len(".tgz")]
1060 if archive_name[-len(".tar.gz"):] == ".tar.gz":
1061 archive_name = archive_name[:-len(".tar.gz")]
1064 dir_name = package_default_path
1065 if package_type == BINARY:
1066 archive_name = (runner.cfg.APPLICATION.name +
1068 runner.cfg.VARS.dist)
1070 if package_type == SOURCE:
1071 archive_name = (runner.cfg.APPLICATION.name +
1074 if options.with_vcs:
1075 archive_name = (runner.cfg.APPLICATION.name +
1081 if package_type == PROJECT:
1082 project_name, __ = os.path.splitext(
1083 os.path.basename(options.project))
1084 archive_name = ("PROJECT" +
1088 if package_type == SAT:
1089 archive_name = ("salomeTools" +
1091 runner.cfg.INTERNAL.sat_version)
1093 path_targz = os.path.join(dir_name, archive_name + ".tgz")
1095 # Print the path of the package
1096 src.printcolors.print_value(logger, "Package path", path_targz, 2)
1098 # Create a working directory for all files that are produced during the
1099 # package creation and that will be removed at the end of the command
1100 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
1101 runner.cfg.VARS.datehour)
1102 src.ensure_path_exists(tmp_working_dir)
1103 logger.write("\n", 5)
1104 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
1106 logger.write("\n", 3)
1108 msg = _("Preparation of files to add to the archive")
1109 logger.write(src.printcolors.printcLabel(msg), 2)
1110 logger.write("\n", 2)
1112 if package_type == BINARY:
1113 d_files_to_add = binary_package(runner.cfg,
1117 if not(d_files_to_add):
1120 if package_type == SOURCE:
1121 d_files_to_add = source_package(runner,
1127 if package_type == PROJECT:
1128 d_files_to_add = project_package(options.project, tmp_working_dir)
1130 if package_type == SAT:
1131 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
1133 # Add the README file in the package
1134 local_readme_tmp_path = add_readme(runner.cfg,
1137 d_files_to_add["README"] = (local_readme_tmp_path, "README")
1139 # Add the additional files of option add_files
1140 if options.add_files:
1141 for file_path in options.add_files:
1142 if not os.path.exists(file_path):
1143 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1145 file_name = os.path.basename(file_path)
1146 d_files_to_add[file_name] = (file_path, file_name)
1148 logger.write("\n", 2)
1150 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1151 logger.write("\n", 2)
1154 # Creating the object tarfile
1155 tar = tarfile.open(path_targz, mode='w:gz')
1157 # get the filtering function if needed
1158 filter_function = None
1159 if package_type == BINARY:
1160 filter_function = exclude_VCS_info
1162 # Add the files to the tarfile object
1163 res = add_files(tar, archive_name, d_files_to_add, logger, f_exclude=filter_function)
1165 except KeyboardInterrupt:
1166 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1167 logger.write(_("Removing the temporary working directory ... "), 1)
1168 # remove the working directory
1169 shutil.rmtree(tmp_working_dir)
1170 logger.write(_("OK"), 1)
1171 logger.write(_("\n"), 1)
1174 # remove the working directory
1175 shutil.rmtree(tmp_working_dir)
1177 # Print again the path of the package
1178 logger.write("\n", 2)
1179 src.printcolors.print_value(logger, "Package path", path_targz, 2)