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):
707 find_product_scripts_and_pyconf(p_name,
711 compil_scripts_tmp_dir,
714 products_pyconf_tmp_dir)
716 find_application_pyconf(config, application_tmp_dir)
718 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
721 def find_product_scripts_and_pyconf(p_name,
725 compil_scripts_tmp_dir,
728 products_pyconf_tmp_dir):
729 '''Create a specific pyconf file for a given product. Get its environment
730 script, its compilation script and patches and put it in the temporary
731 working directory. This method is used in the source package in order to
732 construct the specific project.
734 :param p_name str: The name of the product.
735 :param p_info Config: The specific configuration corresponding to the
737 :param config Config: The global configuration.
738 :param with_vcs boolean: True if the package is with vcs products (not
739 transformed into archive products)
740 :param compil_scripts_tmp_dir str: The path to the temporary compilation
741 scripts directory of the project.
742 :param env_scripts_tmp_dir str: The path to the temporary environment script
743 directory of the project.
744 :param patches_tmp_dir str: The path to the temporary patch scripts
745 directory of the project.
746 :param products_pyconf_tmp_dir str: The path to the temporary product
747 scripts directory of the project.
750 # read the pyconf of the product
751 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
752 config.PATHS.PRODUCTPATH)
753 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
755 # find the compilation script if any
756 if src.product.product_has_script(p_info):
757 compil_script_path = src.Path(p_info.compil_script)
758 compil_script_path.copy(compil_scripts_tmp_dir)
759 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
760 p_info.compil_script)
761 # find the environment script if any
762 if src.product.product_has_env_script(p_info):
763 env_script_path = src.Path(p_info.environ.env_script)
764 env_script_path.copy(env_scripts_tmp_dir)
765 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
766 p_info.environ.env_script)
767 # find the patches if any
768 if src.product.product_has_patches(p_info):
769 patches = src.pyconf.Sequence()
770 for patch_path in p_info.patches:
771 p_path = src.Path(patch_path)
772 p_path.copy(patches_tmp_dir)
773 patches.append(os.path.basename(patch_path), "")
775 product_pyconf_cfg[p_info.section].patches = patches
778 # put in the pyconf file the resolved values
779 for info in ["git_info", "cvs_info", "svn_info"]:
781 for key in p_info[info]:
782 product_pyconf_cfg[p_info.section][info][key] = p_info[
785 # if the product is not archive, then make it become archive.
786 if src.product.product_is_vcs(p_info):
787 product_pyconf_cfg[p_info.section].get_source = "archive"
788 if not "archive_info" in product_pyconf_cfg[p_info.section]:
789 product_pyconf_cfg[p_info.section].addMapping("archive_info",
790 src.pyconf.Mapping(product_pyconf_cfg),
792 product_pyconf_cfg[p_info.section
793 ].archive_info.archive_name = p_info.name + ".tgz"
795 # write the pyconf file to the temporary project location
796 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
798 ff = open(product_tmp_pyconf_path, 'w')
799 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
800 product_pyconf_cfg.__save__(ff, 1)
803 def find_application_pyconf(config, application_tmp_dir):
804 '''Find the application pyconf file and put it in the specific temporary
805 directory containing the specific project of a source package.
807 :param config Config: The global configuration.
808 :param application_tmp_dir str: The path to the temporary application
809 scripts directory of the project.
811 # read the pyconf of the application
812 application_name = config.VARS.application
813 application_pyconf_path = src.find_file_in_lpath(
814 application_name + ".pyconf",
815 config.PATHS.APPLICATIONPATH)
816 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
819 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
820 application_pyconf_cfg,
822 'VARS.salometoolsway + $VARS.sep + ".."')
824 # Prevent from compilation in base
825 application_pyconf_cfg.APPLICATION.no_base = "yes"
827 # write the pyconf file to the temporary application location
828 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
829 application_name + ".pyconf")
830 ff = open(application_tmp_pyconf_path, 'w')
831 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
832 application_pyconf_cfg.__save__(ff, 1)
835 def project_package(project_file_path, tmp_working_dir):
836 '''Prepare a dictionary that stores all the needed directories and files to
837 add in a project package.
839 :param project_file_path str: The path to the local project.
840 :param tmp_working_dir str: The temporary local directory containing some
841 specific directories or files needed in the
843 :return: the dictionary that stores all the needed directories and files to
844 add in a project package.
845 {label : (path_on_local_machine, path_in_archive)}
849 # Read the project file and get the directories to add to the package
850 project_pyconf_cfg = src.pyconf.Config(project_file_path)
851 paths = {"ARCHIVEPATH" : "archives",
852 "APPLICATIONPATH" : "applications",
853 "PRODUCTPATH" : "products",
855 "MACHINEPATH" : "machines"}
856 # Loop over the project paths and add it
858 if path not in project_pyconf_cfg:
860 # Add the directory to the files to add in the package
861 d_project[path] = (project_pyconf_cfg[path], paths[path])
862 # Modify the value of the path in the package
863 project_pyconf_cfg[path] = src.pyconf.Reference(
866 'project_path + "/' + paths[path] + '"')
869 if "project_path" not in project_pyconf_cfg:
870 project_pyconf_cfg.addMapping("project_path",
871 src.pyconf.Mapping(project_pyconf_cfg),
873 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
877 # Write the project pyconf file
878 project_file_name = os.path.basename(project_file_path)
879 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
880 ff = open(project_pyconf_tmp_path, 'w')
881 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
882 project_pyconf_cfg.__save__(ff, 1)
884 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
888 def add_readme(config, package_type, where):
889 readme_path = os.path.join(where, "README")
890 f = open(readme_path, 'w')
891 # prepare substitution dictionary
893 if package_type == BINARY:
894 d['application'] = config.VARS.application
895 d['user'] = config.VARS.user
896 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
897 d['version'] = config.INTERNAL.sat_version
898 d['dist'] = config.VARS.dist
899 if 'profile' in config.APPLICATION:
900 d['launcher'] = config.APPLICATION.profile.launcher_name
901 readme_template_path = os.path.join(config.VARS.internal_dir,
902 "README_BIN.template")
904 d['env_file'] = 'env_launch.sh'
905 readme_template_path = os.path.join(config.VARS.internal_dir,
906 "README_BIN_NO_PROFILE.template")
908 if package_type == SOURCE:
909 d['application'] = config.VARS.application
910 d['user'] = config.VARS.user
911 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
912 d['version'] = config.INTERNAL.sat_version
913 if 'profile' in config.APPLICATION:
914 d['profile'] = config.APPLICATION.profile.product
915 d['launcher'] = config.APPLICATION.profile.launcher_name
916 readme_template_path = os.path.join(config.VARS.internal_dir,
917 "README_SRC.template")
919 if package_type == PROJECT:
920 d['user'] = config.VARS.user
921 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
922 d['version'] = config.INTERNAL.sat_version
923 readme_template_path = os.path.join(config.VARS.internal_dir,
924 "README_PROJECT.template")
926 if package_type == SAT:
927 d['user'] = config.VARS.user
928 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
929 d['version'] = config.INTERNAL.sat_version
930 readme_template_path = os.path.join(config.VARS.internal_dir,
931 "README_SAT.template")
933 f.write(src.template.substitute(readme_template_path, d))
937 def update_config(config, prop, value):
938 '''Remove from config.APPLICATION.products the products that have the property given as input.
940 :param config Config: The global config.
941 :param prop str: The property to filter
942 :param value str: The value of the property to filter
944 src.check_config_has_application(config)
945 l_product_to_remove = []
946 for product_name in config.APPLICATION.products.keys():
947 prod_cfg = src.product.get_product_config(config, product_name)
948 if src.get_property_in_product_cfg(prod_cfg, prop) == value:
949 l_product_to_remove.append(product_name)
950 for product_name in l_product_to_remove:
951 config.APPLICATION.products.__delitem__(product_name)
954 '''method that is called when salomeTools is called with --help option.
956 :return: The text to display for the package command description.
959 return _("The package command creates an archive.\nThere are 4 kinds of "
960 "archive:\n 1- The binary archive. It contains all the product "
961 "installation directories and a launcher,\n 2- The sources archive."
962 " It contains the products archives, a project corresponding to "
963 "the application and salomeTools,\n 3- The project archive. It "
964 "contains a project (give the project file path as argument),\n 4-"
965 " The salomeTools archive. It contains salomeTools.\n\nexample:"
966 "\nsat package SALOME-master --sources")
968 def run(args, runner, logger):
969 '''method that is called when salomeTools is called with package parameter.
973 (options, args) = parser.parse_args(args)
975 # Check that a type of package is called, and only one
976 all_option_types = (options.binaries,
978 options.project not in ["", None],
981 # Check if no option for package type
982 if all_option_types.count(True) == 0:
983 msg = _("Error: Precise a type for the package\nUse one of the "
984 "following options: --binaries, --sources, --project or"
986 logger.write(src.printcolors.printcError(msg), 1)
987 logger.write("\n", 1)
990 # Check for only one option for package type
991 if all_option_types.count(True) > 1:
992 msg = _("Error: You can use only one type for the package\nUse only one"
993 " of the following options: --binaries, --sources, --project or"
995 logger.write(src.printcolors.printcError(msg), 1)
996 logger.write("\n", 1)
999 # Get the package type
1000 if options.binaries:
1001 package_type = BINARY
1003 package_type = SOURCE
1005 package_type = PROJECT
1009 # The repository where to put the package if not Binary or Source
1010 package_default_path = runner.cfg.USER.workdir
1012 if package_type in [BINARY, SOURCE]:
1013 # Check that the command has been called with an application
1014 src.check_config_has_application(runner.cfg)
1016 # Display information
1017 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
1018 runner.cfg.VARS.application), 1)
1020 # Get the default directory where to put the packages
1021 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
1023 src.ensure_path_exists(package_default_path)
1025 elif package_type == PROJECT:
1026 # check that the project is visible by SAT
1027 if options.project not in runner.cfg.PROJECTS.project_file_paths:
1028 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
1031 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
1032 "\nPlease add it in the %(site)s file." % {
1033 "proj" : options.project, "site" : site_path})
1034 logger.write(src.printcolors.printcError(msg), 1)
1035 logger.write("\n", 1)
1038 # Remove the products that are filtered by the --without_property option
1039 if options.without_property:
1040 [prop, value] = options.without_property.split(":")
1041 update_config(runner.cfg, prop, value)
1044 src.printcolors.print_value(logger, "Package type", package_type, 2)
1046 # get the name of the archive or construct it
1048 if os.path.basename(options.name) == options.name:
1049 # only a name (not a path)
1050 archive_name = options.name
1051 dir_name = package_default_path
1053 archive_name = os.path.basename(options.name)
1054 dir_name = os.path.dirname(options.name)
1056 # suppress extension
1057 if archive_name[-len(".tgz"):] == ".tgz":
1058 archive_name = archive_name[:-len(".tgz")]
1059 if archive_name[-len(".tar.gz"):] == ".tar.gz":
1060 archive_name = archive_name[:-len(".tar.gz")]
1063 dir_name = package_default_path
1064 if package_type == BINARY:
1065 archive_name = (runner.cfg.APPLICATION.name +
1067 runner.cfg.VARS.dist)
1069 if package_type == SOURCE:
1070 archive_name = (runner.cfg.APPLICATION.name +
1073 if options.with_vcs:
1074 archive_name = (runner.cfg.APPLICATION.name +
1080 if package_type == PROJECT:
1081 project_name, __ = os.path.splitext(
1082 os.path.basename(options.project))
1083 archive_name = ("PROJECT" +
1087 if package_type == SAT:
1088 archive_name = ("salomeTools" +
1090 runner.cfg.INTERNAL.sat_version)
1092 path_targz = os.path.join(dir_name, archive_name + ".tgz")
1094 # Print the path of the package
1095 src.printcolors.print_value(logger, "Package path", path_targz, 2)
1097 # Create a working directory for all files that are produced during the
1098 # package creation and that will be removed at the end of the command
1099 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
1100 runner.cfg.VARS.datehour)
1101 src.ensure_path_exists(tmp_working_dir)
1102 logger.write("\n", 5)
1103 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
1105 logger.write("\n", 3)
1107 msg = _("Preparation of files to add to the archive")
1108 logger.write(src.printcolors.printcLabel(msg), 2)
1109 logger.write("\n", 2)
1111 if package_type == BINARY:
1112 d_files_to_add = binary_package(runner.cfg,
1116 if not(d_files_to_add):
1119 if package_type == SOURCE:
1120 d_files_to_add = source_package(runner,
1126 if package_type == PROJECT:
1127 d_files_to_add = project_package(options.project, tmp_working_dir)
1129 if package_type == SAT:
1130 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
1132 # Add the README file in the package
1133 local_readme_tmp_path = add_readme(runner.cfg,
1136 d_files_to_add["README"] = (local_readme_tmp_path, "README")
1138 # Add the additional files of option add_files
1139 if options.add_files:
1140 for file_path in options.add_files:
1141 if not os.path.exists(file_path):
1142 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1144 file_name = os.path.basename(file_path)
1145 d_files_to_add[file_name] = (file_path, file_name)
1147 logger.write("\n", 2)
1149 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1150 logger.write("\n", 2)
1153 # Creating the object tarfile
1154 tar = tarfile.open(path_targz, mode='w:gz')
1156 # get the filtering function if needed
1157 filter_function = None
1158 if package_type == BINARY:
1159 filter_function = exclude_VCS_info
1161 # Add the files to the tarfile object
1162 res = add_files(tar, archive_name, d_files_to_add, logger, f_exclude=filter_function)
1164 except KeyboardInterrupt:
1165 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1166 logger.write(_("Removing the temporary working directory ... "), 1)
1167 # remove the working directory
1168 shutil.rmtree(tmp_working_dir)
1169 logger.write(_("OK"), 1)
1170 logger.write(_("\n"), 1)
1173 # remove the working directory
1174 shutil.rmtree(tmp_working_dir)
1176 # Print again the path of the package
1177 logger.write("\n", 2)
1178 src.printcolors.print_value(logger, "Package path", path_targz, 2)