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
30 from application import get_SALOME_modules
31 import src.debug as DBG
38 ARCHIVE_DIR = "ARCHIVES"
39 PROJECT_DIR = "PROJECT"
41 IGNORED_DIRS = [".git", ".svn"]
42 IGNORED_EXTENSIONS = []
44 PROJECT_TEMPLATE = """#!/usr/bin/env python
47 # The path to the archive root directory
48 root_path : $PWD + "/../"
50 project_path : $PWD + "/"
52 # Where to search the archives of the products
53 ARCHIVEPATH : $root_path + "ARCHIVES"
54 # Where to search the pyconf of the applications
55 APPLICATIONPATH : $project_path + "applications/"
56 # Where to search the pyconf of the products
57 PRODUCTPATH : $project_path + "products/"
58 # Where to search the pyconf of the jobs of the project
59 JOBPATH : $project_path + "jobs/"
60 # Where to search the pyconf of the machines of the project
61 MACHINEPATH : $project_path + "machines/"
64 LOCAL_TEMPLATE = ("""#!/usr/bin/env python
72 archive_dir : 'default'
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: Do not make archive for products in VCS mode (git, cvs, svn). '
96 'Sat prepare will use VCS mode instead to retrieve them'),
98 parser.add_option('', 'ftp', 'boolean', 'ftp',
99 _('Optional: Do not embed archives for products in archive mode.'
100 'Sat prepare will use ftp instead to retrieve them'),
102 parser.add_option('p', 'project', 'string', 'project',
103 _('Optional: Produce an archive that contains a project.'), "")
104 parser.add_option('t', 'salometools', 'boolean', 'sat',
105 _('Optional: Produce an archive that contains salomeTools.'), False)
106 parser.add_option('n', 'name', 'string', 'name',
107 _('Optional: The name or full path of the archive.'), None)
108 parser.add_option('', 'add_files', 'list2', 'add_files',
109 _('Optional: The list of additional files to add to the archive.'), [])
110 parser.add_option('', 'without_commercial', 'boolean', 'without_commercial',
111 _('Optional: do not add commercial licence.'), False)
112 parser.add_option('', 'without_properties', 'properties', 'without_properties',
113 _('Optional: Filter the products by their properties.\n\tSyntax: '
114 '--without_properties <property>:<value>'))
117 def add_files(tar, name_archive, d_content, logger, f_exclude=None):
118 '''Create an archive containing all directories and files that are given in
119 the d_content argument.
121 :param tar tarfile: The tarfile instance used to make the archive.
122 :param name_archive str: The name of the archive to make.
123 :param d_content dict: The dictionary that contain all directories and files
124 to add in the archive.
126 (path_on_local_machine, path_in_archive)
127 :param logger Logger: the logging instance
128 :param f_exclude Function: the function that filters
129 :return: 0 if success, 1 if not.
132 # get the max length of the messages in order to make the display
133 max_len = len(max(d_content.keys(), key=len))
136 # loop over each directory or file stored in the d_content dictionary
137 names = sorted(d_content.keys())
138 DBG.write("add tar names", names)
141 # display information
142 len_points = max_len - len(name) + 3
143 local_path, archive_path = d_content[name]
144 in_archive = os.path.join(name_archive, archive_path)
145 logger.write(name + " " + len_points * "." + " "+ in_archive + " ", 3)
146 # Get the local path and the path in archive
147 # of the directory or file to add
148 # Add it in the archive
150 tar.add(local_path, arcname=in_archive, exclude=f_exclude)
151 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
152 except Exception as e:
153 logger.write(src.printcolors.printcError(_("KO ")), 3)
154 logger.write(str(e), 3)
156 logger.write("\n", 3)
159 def exclude_VCS_and_extensions(filename):
160 ''' The function that is used to exclude from package the link to the
161 VCS repositories (like .git)
163 :param filename Str: The filname to exclude (or not).
164 :return: True if the file has to be exclude
167 for dir_name in IGNORED_DIRS:
168 if dir_name in filename:
170 for extension in IGNORED_EXTENSIONS:
171 if filename.endswith(extension):
175 def produce_relative_launcher(config,
180 with_commercial=True):
181 '''Create a specific SALOME launcher for the binary package. This launcher
184 :param config Config: The global configuration.
185 :param logger Logger: the logging instance
186 :param file_dir str: the directory where to put the launcher
187 :param file_name str: The launcher name
188 :param binaries_dir_name str: the name of the repository where the binaries
190 :return: the path of the produced launcher
194 # get KERNEL installation path
195 kernel_root_dir = os.path.join(binaries_dir_name, "KERNEL")
197 # set kernel bin dir (considering fhs property)
198 kernel_cfg = src.product.get_product_config(config, "KERNEL")
199 if src.get_property_in_product_cfg(kernel_cfg, "fhs"):
200 bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin")
202 bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin","salome")
204 # check if the application contains an application module
205 # check also if the application has a distene product,
206 # in this case get its licence file name
207 l_product_info = src.product.get_products_infos(config.APPLICATION.products.keys(), config)
208 salome_application_name="Not defined"
209 distene_licence_file_name=False
210 for prod_name, prod_info in l_product_info:
211 # look for a "salome application" and a distene product
212 if src.get_property_in_product_cfg(prod_info, "is_distene") == "yes":
213 distene_licence_file_name = src.product.product_has_licence(prod_info,
214 config.PATHS.LICENCEPATH)
215 if src.get_property_in_product_cfg(prod_info, "is_salome_application") == "yes":
216 salome_application_name=prod_info.name
218 # if the application contains an application module, we set ABSOLUTE_APPLI_PATH to it
219 # if not we set it to KERNEL_INSTALL_DIR, which is sufficient, except for salome test
220 if salome_application_name == "Not defined":
221 app_root_dir=kernel_root_dir
223 app_root_dir=os.path.join(binaries_dir_name, salome_application_name)
225 # Get the launcher template and do substitutions
226 if "python3" in config.APPLICATION and config.APPLICATION.python3 == "yes":
227 withProfile = src.fileEnviron.withProfile3
229 withProfile = src.fileEnviron.withProfile
231 withProfile = withProfile.replace(
232 "ABSOLUTE_APPLI_PATH'] = 'KERNEL_INSTALL_DIR'",
233 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + app_root_dir + "'")
234 withProfile = withProfile.replace(
235 " 'BIN_KERNEL_INSTALL_DIR'",
236 " out_dir_Path + '" + config.VARS.sep + bin_kernel_install_dir + "'")
238 before, after = withProfile.split("# here your local standalone environment\n")
240 # create an environment file writer
241 writer = src.environment.FileEnvWriter(config,
246 filepath = os.path.join(file_dir, file_name)
247 # open the file and write into it
248 launch_file = open(filepath, "w")
249 launch_file.write(before)
251 writer.write_cfgForPy_file(launch_file,
252 for_package = binaries_dir_name,
253 with_commercial=with_commercial)
254 launch_file.write(after)
257 # Little hack to put out_dir_Path outside the strings
258 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
260 # A hack to put a call to a file for distene licence.
261 # It does nothing to an application that has no distene product
262 if distene_licence_file_name:
263 logger.write("Application has a distene licence file! We use it in package launcher", 5)
264 hack_for_distene_licence(filepath, distene_licence_file_name)
266 # change the rights in order to make the file executable for everybody
278 def hack_for_distene_licence(filepath, licence_file):
279 '''Replace the distene licence env variable by a call to a file.
281 :param filepath Str: The path to the launcher to modify.
283 shutil.move(filepath, filepath + "_old")
285 filein = filepath + "_old"
286 fin = open(filein, "r")
287 fout = open(fileout, "w")
288 text = fin.readlines()
289 # Find the Distene section
291 for i,line in enumerate(text):
292 if "# Set DISTENE License" in line:
296 # No distene product, there is nothing to do
302 del text[num_line +1]
303 del text[num_line +1]
304 text_to_insert =""" import imp
306 distene = imp.load_source('distene_licence', '%s')
307 distene.set_distene_variables(context)
309 pass\n""" % licence_file
310 text.insert(num_line + 1, text_to_insert)
317 def produce_relative_env_files(config,
321 '''Create some specific environment files for the binary package. These
322 files use relative paths.
324 :param config Config: The global configuration.
325 :param logger Logger: the logging instance
326 :param file_dir str: the directory where to put the files
327 :param binaries_dir_name str: the name of the repository where the binaries
329 :return: the list of path of the produced environment files
332 # create an environment file writer
333 writer = src.environment.FileEnvWriter(config,
339 filepath = writer.write_env_file("env_launch.sh",
342 for_package = binaries_dir_name)
344 # Little hack to put out_dir_Path as environment variable
345 src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
347 # change the rights in order to make the file executable for everybody
359 def produce_install_bin_file(config,
364 '''Create a bash shell script which do substitutions in BIRARIES dir
365 in order to use it for extra compilations.
367 :param config Config: The global configuration.
368 :param logger Logger: the logging instance
369 :param file_dir str: the directory where to put the files
370 :param d_sub, dict: the dictionnary that contains the substitutions to be done
371 :param file_name str: the name of the install script file
372 :return: the produced file
376 filepath = os.path.join(file_dir, file_name)
377 # open the file and write into it
378 # use codec utf-8 as sat variables are in unicode
379 with codecs.open(filepath, "w", 'utf-8') as installbin_file:
380 installbin_template_path = os.path.join(config.VARS.internal_dir,
381 "INSTALL_BIN.template")
383 # build the name of the directory that will contain the binaries
384 binaries_dir_name = "BINARIES-" + config.VARS.dist
385 # build the substitution loop
386 loop_cmd = "for f in $(grep -RIl"
388 loop_cmd += " -e "+ key
389 loop_cmd += ' INSTALL); do\n sed -i "\n'
391 loop_cmd += " s?" + key + "?$(pwd)/" + d_sub[key] + "?g\n"
392 loop_cmd += ' " $f\ndone'
395 d["BINARIES_DIR"] = binaries_dir_name
396 d["SUBSTITUTION_LOOP"]=loop_cmd
398 # substitute the template and write it in file
399 content=src.template.substitute(installbin_template_path, d)
400 installbin_file.write(content)
401 # change the rights in order to make the file executable for everybody
413 def product_appli_creation_script(config,
417 '''Create a script that can produce an application (EDF style) in the binary
420 :param config Config: The global configuration.
421 :param logger Logger: the logging instance
422 :param file_dir str: the directory where to put the file
423 :param binaries_dir_name str: the name of the repository where the binaries
425 :return: the path of the produced script file
428 template_name = "create_appli.py.for_bin_packages.template"
429 template_path = os.path.join(config.VARS.internal_dir, template_name)
430 text_to_fill = open(template_path, "r").read()
431 text_to_fill = text_to_fill.replace("TO BE FILLED 1",
432 '"' + binaries_dir_name + '"')
435 for product_name in get_SALOME_modules(config):
436 product_info = src.product.get_product_config(config, product_name)
438 if src.product.product_is_smesh_plugin(product_info):
441 if 'install_dir' in product_info and bool(product_info.install_dir):
442 if src.product.product_is_cpp(product_info):
444 for cpp_name in src.product.get_product_components(product_info):
445 line_to_add = ("<module name=\"" +
447 "\" gui=\"yes\" path=\"''' + "
448 "os.path.join(dir_bin_name, \"" +
449 cpp_name + "\") + '''\"/>")
452 line_to_add = ("<module name=\"" +
454 "\" gui=\"yes\" path=\"''' + "
455 "os.path.join(dir_bin_name, \"" +
456 product_name + "\") + '''\"/>")
457 text_to_add += line_to_add + "\n"
459 filled_text = text_to_fill.replace("TO BE FILLED 2", text_to_add)
461 tmp_file_path = os.path.join(file_dir, "create_appli.py")
462 ff = open(tmp_file_path, "w")
463 ff.write(filled_text)
466 # change the rights in order to make the file executable for everybody
467 os.chmod(tmp_file_path,
478 def binary_package(config, logger, options, tmp_working_dir):
479 '''Prepare a dictionary that stores all the needed directories and files to
480 add in a binary package.
482 :param config Config: The global configuration.
483 :param logger Logger: the logging instance
484 :param options OptResult: the options of the launched command
485 :param tmp_working_dir str: The temporary local directory containing some
486 specific directories or files needed in the
488 :return: the dictionary that stores all the needed directories and files to
489 add in a binary package.
490 {label : (path_on_local_machine, path_in_archive)}
494 # Get the list of product installation to add to the archive
495 l_products_name = sorted(config.APPLICATION.products.keys())
496 l_product_info = src.product.get_products_infos(l_products_name,
501 l_sources_not_present = []
502 generate_mesa_launcher = False # a flag to know if we generate a mesa launcher
503 if ("APPLICATION" in config and
504 "properties" in config.APPLICATION and
505 "mesa_launcher_in_package" in config.APPLICATION.properties and
506 config.APPLICATION.properties.mesa_launcher_in_package == "yes") :
507 generate_mesa_launcher=True
509 for prod_name, prod_info in l_product_info:
510 # skip product with property not_in_package set to yes
511 if src.get_property_in_product_cfg(prod_info, "not_in_package") == "yes":
514 # Add the sources of the products that have the property
515 # sources_in_package : "yes"
516 if src.get_property_in_product_cfg(prod_info,
517 "sources_in_package") == "yes":
518 if os.path.exists(prod_info.source_dir):
519 l_source_dir.append((prod_name, prod_info.source_dir))
521 l_sources_not_present.append(prod_name)
523 # ignore the native and fixed products for install directories
524 if (src.product.product_is_native(prod_info)
525 or src.product.product_is_fixed(prod_info)
526 or not src.product.product_compiles(prod_info)):
528 if src.product.check_installation(prod_info):
529 l_install_dir.append((prod_name, prod_info.install_dir))
531 l_not_installed.append(prod_name)
533 # Add also the cpp generated modules (if any)
534 if src.product.product_is_cpp(prod_info):
536 for name_cpp in src.product.get_product_components(prod_info):
537 install_dir = os.path.join(config.APPLICATION.workdir,
539 if os.path.exists(install_dir):
540 l_install_dir.append((name_cpp, install_dir))
542 l_not_installed.append(name_cpp)
544 # check the name of the directory that (could) contains the binaries
545 # from previous detar
546 binaries_from_detar = os.path.join(config.APPLICATION.workdir, "BINARIES-" + config.VARS.dist)
547 if os.path.exists(binaries_from_detar):
549 WARNING: existing binaries directory from previous detar installation:
551 To make new package from this, you have to:
552 1) install binaries in INSTALL directory with the script "install_bin.sh"
553 see README file for more details
554 2) or recompile everything in INSTALL with "sat compile" command
555 this step is long, and requires some linux packages to be installed
557 """ % binaries_from_detar)
559 # Print warning or error if there are some missing products
560 if len(l_not_installed) > 0:
561 text_missing_prods = ""
562 for p_name in l_not_installed:
563 text_missing_prods += "-" + p_name + "\n"
564 if not options.force_creation:
565 msg = _("ERROR: there are missing products installations:")
566 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
571 msg = _("WARNING: there are missing products installations:")
572 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
576 # Do the same for sources
577 if len(l_sources_not_present) > 0:
578 text_missing_prods = ""
579 for p_name in l_sources_not_present:
580 text_missing_prods += "-" + p_name + "\n"
581 if not options.force_creation:
582 msg = _("ERROR: there are missing products sources:")
583 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
588 msg = _("WARNING: there are missing products sources:")
589 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
593 # construct the name of the directory that will contain the binaries
594 binaries_dir_name = "BINARIES-" + config.VARS.dist
596 # construct the correlation table between the product names, there
597 # actual install directories and there install directory in archive
599 for prod_name, install_dir in l_install_dir:
600 path_in_archive = os.path.join(binaries_dir_name, prod_name)
601 d_products[prod_name + " (bin)"] = (install_dir, path_in_archive)
603 for prod_name, source_dir in l_source_dir:
604 path_in_archive = os.path.join("SOURCES", prod_name)
605 d_products[prod_name + " (sources)"] = (source_dir, path_in_archive)
607 # for packages of SALOME applications including KERNEL,
608 # we produce a salome launcher or a virtual application (depending on salome version)
609 if 'KERNEL' in config.APPLICATION.products:
610 VersionSalome = src.get_salome_version(config)
611 # Case where SALOME has the launcher that uses the SalomeContext API
612 if VersionSalome >= 730:
613 # create the relative launcher and add it to the files to add
614 launcher_name = src.get_launcher_name(config)
615 launcher_package = produce_relative_launcher(config,
620 not(options.without_commercial))
621 d_products["launcher"] = (launcher_package, launcher_name)
623 # if the application contains mesa products, we generate in addition to the
624 # classical salome launcher a launcher using mesa and called mesa_salome
625 # (the mesa launcher will be used for remote usage through ssh).
626 if generate_mesa_launcher:
627 #if there is one : store the use_mesa property
628 restore_use_mesa_option=None
629 if ('properties' in config.APPLICATION and
630 'use_mesa' in config.APPLICATION.properties):
631 restore_use_mesa_option = config.APPLICATION.properties.use_mesa
633 # activate mesa property, and generate a mesa launcher
634 src.activate_mesa_property(config) #activate use_mesa property
635 launcher_mesa_name="mesa_"+launcher_name
636 launcher_package_mesa = produce_relative_launcher(config,
641 not(options.without_commercial))
642 d_products["launcher (mesa)"] = (launcher_package_mesa, launcher_mesa_name)
644 # if there was a use_mesa value, we restore it
645 # else we set it to the default value "no"
646 if restore_use_mesa_option != None:
647 config.APPLICATION.properties.use_mesa=restore_use_mesa_option
649 config.APPLICATION.properties.use_mesa="no"
652 # if we mix binaries and sources, we add a copy of the launcher,
653 # prefixed with "bin",in order to avoid clashes
654 d_products["launcher (copy)"] = (launcher_package, "bin"+launcher_name)
656 # Provide a script for the creation of an application EDF style
657 appli_script = product_appli_creation_script(config,
662 d_products["appli script"] = (appli_script, "create_appli.py")
664 # Put also the environment file
665 env_file = produce_relative_env_files(config,
670 d_products["environment file"] = (env_file, "env_launch.sh")
674 def source_package(sat, config, logger, options, tmp_working_dir):
675 '''Prepare a dictionary that stores all the needed directories and files to
676 add in a source package.
678 :param config Config: The global configuration.
679 :param logger Logger: the logging instance
680 :param options OptResult: the options of the launched command
681 :param tmp_working_dir str: The temporary local directory containing some
682 specific directories or files needed in the
684 :return: the dictionary that stores all the needed directories and files to
685 add in a source package.
686 {label : (path_on_local_machine, path_in_archive)}
691 # Get all the products that are prepared using an archive
692 # unless ftp mode is specified (in this case the user of the
693 # archive will get the sources through the ftp mode of sat prepare
695 logger.write("Find archive products ... ")
696 d_archives, l_pinfo_vcs = get_archives(config, logger)
697 logger.write("Done\n")
700 if not options.with_vcs and len(l_pinfo_vcs) > 0:
701 # Make archives with the products that are not prepared using an archive
702 # (git, cvs, svn, etc)
703 logger.write("Construct archives for vcs products ... ")
704 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
709 logger.write("Done\n")
712 logger.write("Create the project ... ")
713 d_project = create_project_for_src_package(config,
717 logger.write("Done\n")
720 tmp_sat = add_salomeTools(config, tmp_working_dir)
721 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
723 # Add a sat symbolic link if not win
724 if not src.architecture.is_windows():
725 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
729 # In the jobs, os.getcwd() can fail
730 t = config.LOCAL.workdir
731 os.chdir(tmp_working_dir)
732 if os.path.lexists(tmp_satlink_path):
733 os.remove(tmp_satlink_path)
734 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
737 d_sat["sat link"] = (tmp_satlink_path, "sat")
739 d_source = src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
742 def get_archives(config, logger):
743 '''Find all the products that are get using an archive and all the products
744 that are get using a vcs (git, cvs, svn) repository.
746 :param config Config: The global configuration.
747 :param logger Logger: the logging instance
748 :return: the dictionary {name_product :
749 (local path of its archive, path in the package of its archive )}
750 and the list of specific configuration corresponding to the vcs
754 # Get the list of product informations
755 l_products_name = config.APPLICATION.products.keys()
756 l_product_info = src.product.get_products_infos(l_products_name,
760 for p_name, p_info in l_product_info:
761 # skip product with property not_in_package set to yes
762 if src.get_property_in_product_cfg(p_info, "not_in_package") == "yes":
764 # ignore the native and fixed products
765 if (src.product.product_is_native(p_info)
766 or src.product.product_is_fixed(p_info)):
768 if p_info.get_source == "archive":
769 archive_path = p_info.archive_info.archive_name
770 archive_name = os.path.basename(archive_path)
772 l_pinfo_vcs.append((p_name, p_info))
774 d_archives[p_name] = (archive_path,
775 os.path.join(ARCHIVE_DIR, archive_name))
776 return d_archives, l_pinfo_vcs
778 def add_salomeTools(config, tmp_working_dir):
779 '''Prepare a version of salomeTools that has a specific local.pyconf file
780 configured for a source package.
782 :param config Config: The global configuration.
783 :param tmp_working_dir str: The temporary local directory containing some
784 specific directories or files needed in the
786 :return: The path to the local salomeTools directory to add in the package
789 # Copy sat in the temporary working directory
790 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
791 sat_running_path = src.Path(config.VARS.salometoolsway)
792 sat_running_path.copy(sat_tmp_path)
794 # Update the local.pyconf file that contains the path to the project
795 local_pyconf_name = "local.pyconf"
796 local_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
797 local_pyconf_file = os.path.join(local_pyconf_dir, local_pyconf_name)
798 # Remove the .pyconf file in the root directory of salomeTools if there is
799 # any. (For example when launching jobs, a pyconf file describing the jobs
800 # can be here and is not useful)
801 files_or_dir_SAT = os.listdir(os.path.join(tmp_working_dir, "salomeTools"))
802 for file_or_dir in files_or_dir_SAT:
803 if file_or_dir.endswith(".pyconf") or file_or_dir.endswith(".txt"):
804 file_path = os.path.join(tmp_working_dir,
809 ff = open(local_pyconf_file, "w")
810 ff.write(LOCAL_TEMPLATE)
813 return sat_tmp_path.path
815 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
816 '''For sources package that require that all products are get using an
817 archive, one has to create some archive for the vcs products.
818 So this method calls the clean and source command of sat and then create
821 :param l_pinfo_vcs List: The list of specific configuration corresponding to
823 :param sat Sat: The Sat instance that can be called to clean and source the
825 :param config Config: The global configuration.
826 :param logger Logger: the logging instance
827 :param tmp_working_dir str: The temporary local directory containing some
828 specific directories or files needed in the
830 :return: the dictionary that stores all the archives to add in the source
831 package. {label : (path_on_local_machine, path_in_archive)}
834 # clean the source directory of all the vcs products, then use the source
835 # command and thus construct an archive that will not contain the patches
836 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
837 if False: # clean is dangerous in user/SOURCES, fixed in tmp_local_working_dir
838 logger.write(_("\nclean sources\n"))
839 args_clean = config.VARS.application
840 args_clean += " --sources --products "
841 args_clean += ",".join(l_prod_names)
842 logger.write("WARNING: get_archives_vcs clean\n '%s'\n" % args_clean, 1)
843 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
846 logger.write(_("get sources\n"))
847 args_source = config.VARS.application
848 args_source += " --products "
849 args_source += ",".join(l_prod_names)
850 svgDir = sat.cfg.APPLICATION.workdir
851 tmp_local_working_dir = os.path.join(sat.cfg.APPLICATION.workdir, "tmp_package") # to avoid too much big files in /tmp
852 sat.cfg.APPLICATION.workdir = tmp_local_working_dir
853 # DBG.write("SSS sat config.APPLICATION.workdir", sat.cfg.APPLICATION, True)
854 # DBG.write("sat config id", id(sat.cfg), True)
855 # shit as config is not same id() as for sat.source()
856 # sat.source(args_source, batch=True, verbose=5, logger_add_link = logger)
858 source.run(args_source, sat, logger) #use this mode as runner.cfg reference
860 # make the new archives
862 for pn, pinfo in l_pinfo_vcs:
863 path_archive = make_archive(pn, pinfo, tmp_local_working_dir)
864 logger.write("make archive vcs '%s'\n" % path_archive)
865 d_archives_vcs[pn] = (path_archive,
866 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
867 sat.cfg.APPLICATION.workdir = svgDir
868 # DBG.write("END sat config", sat.cfg.APPLICATION, True)
869 return d_archives_vcs
871 def make_archive(prod_name, prod_info, where):
872 '''Create an archive of a product by searching its source directory.
874 :param prod_name str: The name of the product.
875 :param prod_info Config: The specific configuration corresponding to the
877 :param where str: The path of the repository where to put the resulting
879 :return: The path of the resulting archive
882 path_targz_prod = os.path.join(where, prod_name + ".tgz")
883 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
884 local_path = prod_info.source_dir
885 tar_prod.add(local_path,
887 exclude=exclude_VCS_and_extensions)
889 return path_targz_prod
891 def create_project_for_src_package(config, tmp_working_dir, with_vcs, with_ftp):
892 '''Create a specific project for a source package.
894 :param config Config: The global configuration.
895 :param tmp_working_dir str: The temporary local directory containing some
896 specific directories or files needed in the
898 :param with_vcs boolean: True if the package is with vcs products (not
899 transformed into archive products)
900 :param with_ftp boolean: True if the package use ftp servers to get archives
901 :return: The dictionary
902 {"project" : (produced project, project path in the archive)}
906 # Create in the working temporary directory the full project tree
907 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
908 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
910 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
913 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
916 patches_tmp_dir = os.path.join(project_tmp_dir,
919 application_tmp_dir = os.path.join(project_tmp_dir,
921 for directory in [project_tmp_dir,
922 compil_scripts_tmp_dir,
925 application_tmp_dir]:
926 src.ensure_path_exists(directory)
928 # Create the pyconf that contains the information of the project
929 project_pyconf_name = "project.pyconf"
930 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
931 ff = open(project_pyconf_file, "w")
932 ff.write(PROJECT_TEMPLATE)
933 if with_ftp and len(config.PATHS.ARCHIVEFTP) > 0:
934 ftp_path='ARCHIVEFTP : "'+config.PATHS.ARCHIVEFTP[0]
935 for ftpserver in config.PATHS.ARCHIVEFTP[1:]:
936 ftp_path=ftp_path+":"+ftpserver
938 ff.write("# ftp servers where to search for prerequisite archives\n")
940 # add licence paths if any
941 if len(config.PATHS.LICENCEPATH) > 0:
942 licence_path='LICENCEPATH : "'+config.PATHS.LICENCEPATH[0]
943 for path in config.PATHS.LICENCEPATH[1:]:
944 licence_path=licence_path+":"+path
946 ff.write("\n# Where to search for licences\n")
947 ff.write(licence_path)
952 # Loop over the products to get there pyconf and all the scripts
953 # (compilation, environment, patches)
954 # and create the pyconf file to add to the project
955 lproducts_name = config.APPLICATION.products.keys()
956 l_products = src.product.get_products_infos(lproducts_name, config)
957 for p_name, p_info in l_products:
958 # skip product with property not_in_package set to yes
959 if src.get_property_in_product_cfg(p_info, "not_in_package") == "yes":
961 find_product_scripts_and_pyconf(p_name,
965 compil_scripts_tmp_dir,
968 products_pyconf_tmp_dir)
970 find_application_pyconf(config, application_tmp_dir)
972 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
975 def find_product_scripts_and_pyconf(p_name,
979 compil_scripts_tmp_dir,
982 products_pyconf_tmp_dir):
983 '''Create a specific pyconf file for a given product. Get its environment
984 script, its compilation script and patches and put it in the temporary
985 working directory. This method is used in the source package in order to
986 construct the specific project.
988 :param p_name str: The name of the product.
989 :param p_info Config: The specific configuration corresponding to the
991 :param config Config: The global configuration.
992 :param with_vcs boolean: True if the package is with vcs products (not
993 transformed into archive products)
994 :param compil_scripts_tmp_dir str: The path to the temporary compilation
995 scripts directory of the project.
996 :param env_scripts_tmp_dir str: The path to the temporary environment script
997 directory of the project.
998 :param patches_tmp_dir str: The path to the temporary patch scripts
999 directory of the project.
1000 :param products_pyconf_tmp_dir str: The path to the temporary product
1001 scripts directory of the project.
1004 # read the pyconf of the product
1005 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
1006 config.PATHS.PRODUCTPATH)
1007 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
1009 # find the compilation script if any
1010 if src.product.product_has_script(p_info):
1011 compil_script_path = src.Path(p_info.compil_script)
1012 compil_script_path.copy(compil_scripts_tmp_dir)
1013 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
1014 p_info.compil_script)
1015 # find the environment script if any
1016 if src.product.product_has_env_script(p_info):
1017 env_script_path = src.Path(p_info.environ.env_script)
1018 env_script_path.copy(env_scripts_tmp_dir)
1019 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
1020 p_info.environ.env_script)
1021 # find the patches if any
1022 if src.product.product_has_patches(p_info):
1023 patches = src.pyconf.Sequence()
1024 for patch_path in p_info.patches:
1025 p_path = src.Path(patch_path)
1026 p_path.copy(patches_tmp_dir)
1027 patches.append(os.path.basename(patch_path), "")
1029 product_pyconf_cfg[p_info.section].patches = patches
1032 # put in the pyconf file the resolved values
1033 for info in ["git_info", "cvs_info", "svn_info"]:
1035 for key in p_info[info]:
1036 product_pyconf_cfg[p_info.section][info][key] = p_info[
1039 # if the product is not archive, then make it become archive.
1040 if src.product.product_is_vcs(p_info):
1041 product_pyconf_cfg[p_info.section].get_source = "archive"
1042 if not "archive_info" in product_pyconf_cfg[p_info.section]:
1043 product_pyconf_cfg[p_info.section].addMapping("archive_info",
1044 src.pyconf.Mapping(product_pyconf_cfg),
1046 product_pyconf_cfg[p_info.section
1047 ].archive_info.archive_name = p_info.name + ".tgz"
1049 # write the pyconf file to the temporary project location
1050 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
1052 ff = open(product_tmp_pyconf_path, 'w')
1053 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
1054 product_pyconf_cfg.__save__(ff, 1)
1057 def find_application_pyconf(config, application_tmp_dir):
1058 '''Find the application pyconf file and put it in the specific temporary
1059 directory containing the specific project of a source package.
1061 :param config Config: The global configuration.
1062 :param application_tmp_dir str: The path to the temporary application
1063 scripts directory of the project.
1065 # read the pyconf of the application
1066 application_name = config.VARS.application
1067 application_pyconf_path = src.find_file_in_lpath(
1068 application_name + ".pyconf",
1069 config.PATHS.APPLICATIONPATH)
1070 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
1072 # Change the workdir
1073 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
1074 application_pyconf_cfg,
1076 'VARS.salometoolsway + $VARS.sep + ".."')
1078 # Prevent from compilation in base
1079 application_pyconf_cfg.APPLICATION.no_base = "yes"
1081 #remove products that are not in config (which were filtered by --without_properties)
1082 for product_name in application_pyconf_cfg.APPLICATION.products.keys():
1083 if product_name not in config.APPLICATION.products.keys():
1084 application_pyconf_cfg.APPLICATION.products.__delitem__(product_name)
1086 # write the pyconf file to the temporary application location
1087 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
1088 application_name + ".pyconf")
1090 ff = open(application_tmp_pyconf_path, 'w')
1091 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
1092 application_pyconf_cfg.__save__(ff, 1)
1095 def project_package(config, name_project, project_file_path, ftp_mode, tmp_working_dir, logger):
1096 '''Prepare a dictionary that stores all the needed directories and files to
1097 add in a project package.
1099 :param project_file_path str: The path to the local project.
1100 :param ftp_mode boolean: Do not embed archives, the archive will rely on ftp mode to retrieve them.
1101 :param tmp_working_dir str: The temporary local directory containing some
1102 specific directories or files needed in the
1104 :return: the dictionary that stores all the needed directories and files to
1105 add in a project package.
1106 {label : (path_on_local_machine, path_in_archive)}
1110 # Read the project file and get the directories to add to the package
1113 project_pyconf_cfg = config.PROJECTS.projects.__getattr__(name_project)
1116 WARNING: inexisting config.PROJECTS.projects.%s, try to read now from:\n%s\n""" % (name_project, project_file_path))
1117 project_pyconf_cfg = src.pyconf.Config(project_file_path)
1118 project_pyconf_cfg.PWD = os.path.dirname(project_file_path)
1120 paths = {"APPLICATIONPATH" : "applications",
1121 "PRODUCTPATH" : "products",
1123 "MACHINEPATH" : "machines"}
1125 paths["ARCHIVEPATH"] = "archives"
1127 # Loop over the project paths and add it
1129 if path not in project_pyconf_cfg:
1131 # Add the directory to the files to add in the package
1132 d_project[path] = (project_pyconf_cfg[path], paths[path])
1133 # Modify the value of the path in the package
1134 project_pyconf_cfg[path] = src.pyconf.Reference(
1137 'project_path + "/' + paths[path] + '"')
1139 # Modify some values
1140 if "project_path" not in project_pyconf_cfg:
1141 project_pyconf_cfg.addMapping("project_path",
1142 src.pyconf.Mapping(project_pyconf_cfg),
1144 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
1148 # Write the project pyconf file
1149 project_file_name = os.path.basename(project_file_path)
1150 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
1151 ff = open(project_pyconf_tmp_path, 'w')
1152 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
1153 project_pyconf_cfg.__save__(ff, 1)
1155 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
1159 def add_readme(config, options, where):
1160 readme_path = os.path.join(where, "README")
1161 with codecs.open(readme_path, "w", 'utf-8') as f:
1163 # templates for building the header
1165 # This package was generated with sat $version
1168 # Distribution : $dist
1170 In the following, $$ROOT represents the directory where you have installed
1171 SALOME (the directory where this file is located).
1174 readme_compilation_with_binaries="""
1176 compilation based on the binaries used as prerequisites
1177 =======================================================
1179 If you fail to compile the complete application (for example because
1180 you are not root on your system and cannot install missing packages), you
1181 may try a partial compilation based on the binaries.
1182 For that it is necessary to copy the binaries from BINARIES to INSTALL,
1183 and do some substitutions on cmake and .la files (replace the build directories
1185 The procedure to do it is:
1186 1) Remove or rename INSTALL directory if it exists
1187 2) Execute the shell script install_bin.sh:
1190 3) Use SalomeTool (as explained in Sources section) and compile only the
1191 modules you need to (with -p option)
1194 readme_header_tpl=string.Template(readme_header)
1195 readme_template_path_bin = os.path.join(config.VARS.internal_dir,
1196 "README_BIN.template")
1197 readme_template_path_bin_launcher = os.path.join(config.VARS.internal_dir,
1198 "README_LAUNCHER.template")
1199 readme_template_path_bin_virtapp = os.path.join(config.VARS.internal_dir,
1200 "README_BIN_VIRTUAL_APP.template")
1201 readme_template_path_src = os.path.join(config.VARS.internal_dir,
1202 "README_SRC.template")
1203 readme_template_path_pro = os.path.join(config.VARS.internal_dir,
1204 "README_PROJECT.template")
1205 readme_template_path_sat = os.path.join(config.VARS.internal_dir,
1206 "README_SAT.template")
1208 # prepare substitution dictionary
1210 d['user'] = config.VARS.user
1211 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
1212 d['version'] = src.get_salometool_version(config)
1213 d['dist'] = config.VARS.dist
1214 f.write(readme_header_tpl.substitute(d)) # write the general header (common)
1216 if options.binaries or options.sources:
1217 d['application'] = config.VARS.application
1218 f.write("# Application: " + d['application'] + "\n")
1219 if 'KERNEL' in config.APPLICATION.products:
1220 VersionSalome = src.get_salome_version(config)
1221 # Case where SALOME has the launcher that uses the SalomeContext API
1222 if VersionSalome >= 730:
1223 d['launcher'] = config.APPLICATION.profile.launcher_name
1225 d['virtual_app'] = 'runAppli' # this info is not used now)
1227 # write the specific sections
1228 if options.binaries:
1229 f.write(src.template.substitute(readme_template_path_bin, d))
1230 if "virtual_app" in d:
1231 f.write(src.template.substitute(readme_template_path_bin_virtapp, d))
1233 f.write(src.template.substitute(readme_template_path_bin_launcher, d))
1236 f.write(src.template.substitute(readme_template_path_src, d))
1238 if options.binaries and options.sources:
1239 f.write(readme_compilation_with_binaries)
1242 f.write(src.template.substitute(readme_template_path_pro, d))
1245 f.write(src.template.substitute(readme_template_path_sat, d))
1249 def update_config(config, prop, value):
1250 '''Remove from config.APPLICATION.products the products that have the property given as input.
1252 :param config Config: The global config.
1253 :param prop str: The property to filter
1254 :param value str: The value of the property to filter
1256 # if there is no APPLICATION (ex sat package -t) : nothing to do
1257 if "APPLICATION" in config:
1258 l_product_to_remove = []
1259 for product_name in config.APPLICATION.products.keys():
1260 prod_cfg = src.product.get_product_config(config, product_name)
1261 if src.get_property_in_product_cfg(prod_cfg, prop) == value:
1262 l_product_to_remove.append(product_name)
1263 for product_name in l_product_to_remove:
1264 config.APPLICATION.products.__delitem__(product_name)
1267 '''method that is called when salomeTools is called with --help option.
1269 :return: The text to display for the package command description.
1273 The package command creates a tar file archive of a product.
1274 There are four kinds of archive, which can be mixed:
1276 1 - The binary archive.
1277 It contains the product installation directories plus a launcher.
1278 2 - The sources archive.
1279 It contains the product archives, a project (the application plus salomeTools).
1280 3 - The project archive.
1281 It contains a project (give the project file path as argument).
1282 4 - The salomeTools archive.
1283 It contains code utility salomeTools.
1286 >> sat package SALOME-master --binaries --sources""")
1288 def run(args, runner, logger):
1289 '''method that is called when salomeTools is called with package parameter.
1293 (options, args) = parser.parse_args(args)
1295 # Check that a type of package is called, and only one
1296 all_option_types = (options.binaries,
1298 options.project not in ["", None],
1301 # Check if no option for package type
1302 if all_option_types.count(True) == 0:
1303 msg = _("Error: Precise a type for the package\nUse one of the "
1304 "following options: --binaries, --sources, --project or"
1306 logger.write(src.printcolors.printcError(msg), 1)
1307 logger.write("\n", 1)
1310 # The repository where to put the package if not Binary or Source
1311 package_default_path = runner.cfg.LOCAL.workdir
1313 # if the package contains binaries or sources:
1314 if options.binaries or options.sources:
1315 # Check that the command has been called with an application
1316 src.check_config_has_application(runner.cfg)
1318 # Display information
1319 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
1320 runner.cfg.VARS.application), 1)
1322 # Get the default directory where to put the packages
1323 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir, "PACKAGE")
1324 src.ensure_path_exists(package_default_path)
1326 # if the package contains a project:
1328 # check that the project is visible by SAT
1329 projectNameFile = options.project + ".pyconf"
1331 for i in runner.cfg.PROJECTS.project_file_paths:
1332 baseName = os.path.basename(i)
1333 if baseName == projectNameFile:
1337 if foundProject is None:
1338 local_path = os.path.join(runner.cfg.VARS.salometoolsway, "data", "local.pyconf")
1339 msg = _("""ERROR: the project %(1)s is not visible by salomeTools.
1343 Please add it in file:
1345 {"1": options.project, "2": "\n ".join(runner.cfg.PROJECTS.project_file_paths), "3": local_path})
1346 logger.write(src.printcolors.printcError(msg), 1)
1347 logger.write("\n", 1)
1350 options.project_file_path = foundProject
1351 src.printcolors.print_value(logger, "Project path", options.project_file_path, 2)
1353 # Remove the products that are filtered by the --without_properties option
1354 if options.without_properties:
1355 app = runner.cfg.APPLICATION
1356 logger.trace("without_properties all products:\n %s\n" % PP.pformat(sorted(app.products.keys())))
1357 prop, value = options.without_properties
1358 update_config(runner.cfg, prop, value)
1359 logger.warning("without_properties selected products:\n %s\n" % PP.pformat(sorted(app.products.keys())))
1361 # Remove from config the products that have the not_in_package property
1362 update_config(runner.cfg, "not_in_package", "yes")
1364 # get the name of the archive or build it
1366 if os.path.basename(options.name) == options.name:
1367 # only a name (not a path)
1368 archive_name = options.name
1369 dir_name = package_default_path
1371 archive_name = os.path.basename(options.name)
1372 dir_name = os.path.dirname(options.name)
1374 # suppress extension
1375 if archive_name[-len(".tgz"):] == ".tgz":
1376 archive_name = archive_name[:-len(".tgz")]
1377 if archive_name[-len(".tar.gz"):] == ".tar.gz":
1378 archive_name = archive_name[:-len(".tar.gz")]
1382 dir_name = package_default_path
1383 if options.binaries or options.sources:
1384 archive_name = runner.cfg.APPLICATION.name
1386 if options.binaries:
1387 archive_name += "-"+runner.cfg.VARS.dist
1390 archive_name += "-SRC"
1391 if options.with_vcs:
1392 archive_name += "-VCS"
1395 project_name = options.project
1396 archive_name += ("PROJECT-" + project_name)
1399 archive_name += ("salomeTools_" + src.get_salometool_version(runner.cfg))
1400 if len(archive_name)==0: # no option worked
1401 msg = _("Error: Cannot name the archive\n"
1402 " check if at least one of the following options was "
1403 "selected : --binaries, --sources, --project or"
1405 logger.write(src.printcolors.printcError(msg), 1)
1406 logger.write("\n", 1)
1409 path_targz = os.path.join(dir_name, archive_name + ".tgz")
1411 src.printcolors.print_value(logger, "Package path", path_targz, 2)
1413 # Create a working directory for all files that are produced during the
1414 # package creation and that will be removed at the end of the command
1415 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root, runner.cfg.VARS.datehour)
1416 src.ensure_path_exists(tmp_working_dir)
1417 logger.write("\n", 5)
1418 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
1420 logger.write("\n", 3)
1422 msg = _("Preparation of files to add to the archive")
1423 logger.write(src.printcolors.printcLabel(msg), 2)
1424 logger.write("\n", 2)
1426 d_files_to_add={} # content of the archive
1428 # a dict to hold paths that will need to be substitute for users recompilations
1429 d_paths_to_substitute={}
1431 if options.binaries:
1432 d_bin_files_to_add = binary_package(runner.cfg,
1436 # for all binaries dir, store the substitution that will be required
1437 # for extra compilations
1438 for key in d_bin_files_to_add:
1439 if key.endswith("(bin)"):
1440 source_dir = d_bin_files_to_add[key][0]
1441 path_in_archive = d_bin_files_to_add[key][1].replace("BINARIES-" + runner.cfg.VARS.dist,"INSTALL")
1442 if os.path.basename(source_dir)==os.path.basename(path_in_archive):
1443 # if basename is the same we will just substitute the dirname
1444 d_paths_to_substitute[os.path.dirname(source_dir)]=\
1445 os.path.dirname(path_in_archive)
1447 d_paths_to_substitute[source_dir]=path_in_archive
1449 d_files_to_add.update(d_bin_files_to_add)
1452 d_files_to_add.update(source_package(runner,
1457 if options.binaries:
1458 # for archives with bin and sources we provide a shell script able to
1459 # install binaries for compilation
1460 file_install_bin=produce_install_bin_file(runner.cfg,logger,
1462 d_paths_to_substitute,
1464 d_files_to_add.update({"install_bin" : (file_install_bin, "install_bin.sh")})
1465 logger.write("substitutions that need to be done later : \n", 5)
1466 logger.write(str(d_paths_to_substitute), 5)
1467 logger.write("\n", 5)
1469 # --salomeTool option is not considered when --sources is selected, as this option
1470 # already brings salomeTool!
1472 d_files_to_add.update({"salomeTools" : (runner.cfg.VARS.salometoolsway, "")})
1475 DBG.write("config for package %s" % project_name, runner.cfg)
1476 d_files_to_add.update(project_package(runner.cfg, project_name, options.project_file_path, options.ftp, tmp_working_dir, logger))
1478 if not(d_files_to_add):
1479 msg = _("Error: Empty dictionnary to build the archive!\n")
1480 logger.write(src.printcolors.printcError(msg), 1)
1481 logger.write("\n", 1)
1484 # Add the README file in the package
1485 local_readme_tmp_path = add_readme(runner.cfg, options, tmp_working_dir)
1486 d_files_to_add["README"] = (local_readme_tmp_path, "README")
1488 # Add the additional files of option add_files
1489 if options.add_files:
1490 for file_path in options.add_files:
1491 if not os.path.exists(file_path):
1492 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1494 file_name = os.path.basename(file_path)
1495 d_files_to_add[file_name] = (file_path, file_name)
1497 logger.write("\n", 2)
1498 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1499 logger.write("\n", 2)
1500 logger.write("\nfiles and directories to add:\n%s\n\n" % PP.pformat(d_files_to_add), 5)
1504 # Creating the object tarfile
1505 tar = tarfile.open(path_targz, mode='w:gz')
1507 # get the filtering function if needed
1508 filter_function = exclude_VCS_and_extensions
1510 # Add the files to the tarfile object
1511 res = add_files(tar, archive_name, d_files_to_add, logger, f_exclude=filter_function)
1513 except KeyboardInterrupt:
1514 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1515 logger.write(_("Removing the temporary working directory '%s'... ") % tmp_working_dir, 1)
1516 # remove the working directory
1517 shutil.rmtree(tmp_working_dir)
1518 logger.write(_("OK"), 1)
1519 logger.write(_("\n"), 1)
1522 # case if no application, only package sat as 'sat package -t'
1524 app = runner.cfg.APPLICATION
1528 # unconditionaly remove the tmp_local_working_dir
1530 tmp_local_working_dir = os.path.join(app.workdir, "tmp_package")
1531 if os.path.isdir(tmp_local_working_dir):
1532 shutil.rmtree(tmp_local_working_dir)
1534 # have to decide some time
1535 DBG.tofix("make shutil.rmtree('%s') effective" % tmp_working_dir, "", DBG.isDeveloper())
1537 # Print again the path of the package
1538 logger.write("\n", 2)
1539 src.printcolors.print_value(logger, "Package path", path_targz, 2)