3 # Copyright (C) 2010-2012 CEA/DEN
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 from application import get_SALOME_modules
34 ARCHIVE_DIR = "ARCHIVES"
35 PROJECT_DIR = "PROJECT"
37 PROJECT_TEMPLATE = """#!/usr/bin/env python
40 # The path to the archive root directory
41 root_path : $PWD + "/../"
43 project_path : $PWD + "/"
45 # Where to search the archives of the products
46 ARCHIVEPATH : $root_path + "ARCHIVES"
47 # Where to search the pyconf of the applications
48 APPLICATIONPATH : $project_path + "applications/"
49 # Where to search the pyconf of the products
50 PRODUCTPATH : $project_path + "products/"
51 # Where to search the pyconf of the jobs of the project
52 JOBPATH : $project_path + "jobs/"
53 # Where to search the pyconf of the machines of the project
54 MACHINEPATH : $project_path + "machines/"
57 SITE_TEMPLATE = ("""#!/usr/bin/env python
64 log_dir : $USER.workdir + "/LOGS"
67 tmp_dir_with_application : '/tmp' + $VARS.sep + $VARS.user + """
68 """$VARS.sep + $APPLICATION.name + $VARS.sep + 'test'
69 tmp_dir : '/tmp' + $VARS.sep + $VARS.user + $VARS.sep + 'test'
76 project_file_paths : [$VARS.salometoolsway + $VARS.sep + \"..\" + $VARS.sep"""
77 """ + \"""" + PROJECT_DIR + """\" + $VARS.sep + "project.pyconf"]
81 # Define all possible option for the package command : sat package <options>
82 parser = src.options.Options()
83 parser.add_option('b', 'binaries', 'boolean', 'binaries',
84 _('Optional: Produce a binary package.'), False)
85 parser.add_option('f', 'force_creation', 'boolean', 'force_creation',
86 _('Optional: Only binary package: produce the archive even if '
87 'there are some missing products.'), False)
88 parser.add_option('s', 'sources', 'boolean', 'sources',
89 _('Optional: Produce a compilable archive of the sources of the '
90 'application.'), False)
91 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
92 _('Optional: Only source package: do not make archive of vcs products.'),
94 parser.add_option('p', 'project', 'string', 'project',
95 _('Optional: Produce an archive that contains a project.'), "")
96 parser.add_option('t', 'salometools', 'boolean', 'sat',
97 _('Optional: Produce an archive that contains salomeTools.'), False)
98 parser.add_option('n', 'name', 'string', 'name',
99 _('Optional: The name or full path of the archive.'), None)
100 parser.add_option('', 'add_files', 'list2', 'add_files',
101 _('Optional: The list of additional files to add to the archive.'), [])
102 parser.add_option('', 'without_commercial', 'boolean', 'without_commercial',
103 _('Optional: do not add commercial licence.'), False)
105 def add_files(tar, name_archive, d_content, logger):
106 '''Create an archive containing all directories and files that are given in
107 the d_content argument.
109 :param tar tarfile: The tarfile instance used to make the archive.
110 :param name_archive str: The name of the archive to make.
111 :param d_content dict: The dictionary that contain all directories and files
112 to add in the archive.
114 (path_on_local_machine, path_in_archive)
115 :param logger Logger: the logging instance
116 :return: 0 if success, 1 if not.
119 # get the max length of the messages in order to make the display
120 max_len = len(max(d_content.keys(), key=len))
123 # loop over each directory or file stored in the d_content dictionary
124 for name in d_content.keys():
125 # display information
126 len_points = max_len - len(name)
127 logger.write(name + " " + len_points * "." + " ", 3)
128 # Get the local path and the path in archive
129 # of the directory or file to add
130 local_path, archive_path = d_content[name]
131 in_archive = os.path.join(name_archive, archive_path)
132 # Add it in the archive
134 tar.add(local_path, arcname=in_archive)
135 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
136 except Exception as e:
137 logger.write(src.printcolors.printcError(_("KO ")), 3)
138 logger.write(str(e), 3)
140 logger.write("\n", 3)
143 def produce_relative_launcher(config,
148 with_commercial=True):
149 '''Create a specific SALOME launcher for the binary package. This launcher
152 :param config Config: The global configuration.
153 :param logger Logger: the logging instance
154 :param file_dir str: the directory where to put the launcher
155 :param file_name str: The launcher name
156 :param binaries_dir_name str: the name of the repository where the binaries
158 :return: the path of the produced launcher
162 # Get the launcher template
163 profile_install_dir = os.path.join(binaries_dir_name,
164 config.APPLICATION.profile.product)
165 withProfile = src.fileEnviron.withProfile
166 withProfile = withProfile.replace(
167 "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
168 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
169 withProfile = withProfile.replace(
170 "os.path.join( 'PROFILE_INSTALL_DIR'",
171 "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
173 before, after = withProfile.split(
174 "# here your local standalone environment\n")
176 # create an environment file writer
177 writer = src.environment.FileEnvWriter(config,
182 filepath = os.path.join(file_dir, file_name)
183 # open the file and write into it
184 launch_file = open(filepath, "w")
185 launch_file.write(before)
187 writer.write_cfgForPy_file(launch_file,
188 for_package = binaries_dir_name,
189 with_commercial=with_commercial)
190 launch_file.write(after)
193 # Little hack to put out_dir_Path outside the strings
194 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
196 # change the rights in order to make the file executable for everybody
208 def produce_relative_env_files(config,
212 '''Create some specific environment files for the binary package. These
213 files use relative paths.
215 :param config Config: The global configuration.
216 :param logger Logger: the logging instance
217 :param file_dir str: the directory where to put the files
218 :param binaries_dir_name str: the name of the repository where the binaries
220 :return: the list of path of the produced environment files
223 # create an environment file writer
224 writer = src.environment.FileEnvWriter(config,
230 filepath = writer.write_env_file("env_launch.sh",
233 for_package = binaries_dir_name)
235 # Little hack to put out_dir_Path as environment variable
236 src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
238 # change the rights in order to make the file executable for everybody
250 def product_appli_creation_script(config,
254 '''Create a script that can produce an application (EDF style) in the binary
257 :param config Config: The global configuration.
258 :param logger Logger: the logging instance
259 :param file_dir str: the directory where to put the file
260 :param binaries_dir_name str: the name of the repository where the binaries
262 :return: the path of the produced script file
265 template_name = "create_appli.py.for_bin_packages.template"
266 template_path = os.path.join(config.VARS.internal_dir, template_name)
267 text_to_fill = open(template_path, "r").read()
268 text_to_fill = text_to_fill.replace("TO BE FILLED 1",
269 '"' + binaries_dir_name + '"')
272 for product_name in get_SALOME_modules(config):
273 product_info = src.product.get_product_config(config, product_name)
275 if src.product.product_is_smesh_plugin(product_info):
278 if 'install_dir' in product_info and bool(product_info.install_dir):
279 if src.product.product_is_cpp(product_info):
281 for cpp_name in src.product.get_product_components(product_info):
282 line_to_add = ("<module name=\"" +
284 "\" gui=\"yes\" path=\"''' + "
285 "os.path.join(dir_bin_name, \"" +
286 cpp_name + "\") + '''\"/>")
289 line_to_add = ("<module name=\"" +
291 "\" gui=\"yes\" path=\"''' + "
292 "os.path.join(dir_bin_name, \"" +
293 product_name + "\") + '''\"/>")
294 text_to_add += line_to_add + "\n"
296 filled_text = text_to_fill.replace("TO BE FILLED 2", text_to_add)
298 tmp_file_path = os.path.join(file_dir, "create_appli.py")
299 ff = open(tmp_file_path, "w")
300 ff.write(filled_text)
303 # change the rights in order to make the file executable for everybody
304 os.chmod(tmp_file_path,
315 def binary_package(config, logger, options, tmp_working_dir):
316 '''Prepare a dictionary that stores all the needed directories and files to
317 add in a binary package.
319 :param config Config: The global configuration.
320 :param logger Logger: the logging instance
321 :param options OptResult: the options of the launched command
322 :param tmp_working_dir str: The temporary local directory containing some
323 specific directories or files needed in the
325 :return: the dictionary that stores all the needed directories and files to
326 add in a binary package.
327 {label : (path_on_local_machine, path_in_archive)}
331 # Get the list of product installation to add to the archive
332 l_products_name = config.APPLICATION.products.keys()
333 l_product_info = src.product.get_products_infos(l_products_name,
337 for prod_name, prod_info in l_product_info:
338 # ignore the native and fixed products
339 if (src.product.product_is_native(prod_info)
340 or src.product.product_is_fixed(prod_info)
341 or not src.product.product_compiles(prod_info)):
343 if src.product.check_installation(prod_info):
344 l_install_dir.append((prod_name, prod_info.install_dir))
346 l_not_installed.append(prod_name)
348 # Print warning or error if there are some missing products
349 if len(l_not_installed) > 0:
350 text_missing_prods = ""
351 for p_name in l_not_installed:
352 text_missing_prods += "-" + p_name + "\n"
353 if not options.force_creation:
354 msg = _("ERROR: there are missing products installations:")
355 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
360 msg = _("WARNING: there are missing products installations:")
361 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
365 # construct the name of the directory that will contain the binaries
366 binaries_dir_name = "BINARIES-" + config.VARS.dist
368 # construct the correlation table between the product names, there
369 # actual install directories and there install directory in archive
371 for prod_name, install_dir in l_install_dir:
372 path_in_archive = os.path.join(binaries_dir_name, prod_name)
373 d_products[prod_name] = (install_dir, path_in_archive)
375 # create the relative launcher and add it to the files to add
376 if "profile" in config.APPLICATION:
377 launcher_name = config.APPLICATION.profile.launcher_name
378 launcher_package = produce_relative_launcher(config,
383 not(options.without_commercial))
385 d_products["launcher"] = (launcher_package, launcher_name)
387 # No profile, it means that there has to be some environment files
388 env_file = produce_relative_env_files(config,
393 d_products["environment file"] = (env_file, "env_launch.sh")
395 # And provide a script for the creation of an application EDF style
396 appli_script = product_appli_creation_script(config,
401 d_products["appli script"] = (appli_script, "create_appli.py")
405 def source_package(sat, config, logger, options, tmp_working_dir):
406 '''Prepare a dictionary that stores all the needed directories and files to
407 add in a source package.
409 :param config Config: The global configuration.
410 :param logger Logger: the logging instance
411 :param options OptResult: the options of the launched command
412 :param tmp_working_dir str: The temporary local directory containing some
413 specific directories or files needed in the
415 :return: the dictionary that stores all the needed directories and files to
416 add in a source package.
417 {label : (path_on_local_machine, path_in_archive)}
421 # Get all the products that are prepared using an archive
422 logger.write("Find archive products ... ")
423 d_archives, l_pinfo_vcs = get_archives(config, logger)
424 logger.write("Done\n")
426 if not options.with_vcs and len(l_pinfo_vcs) > 0:
427 # Make archives with the products that are not prepared using an archive
428 # (git, cvs, svn, etc)
429 logger.write("Construct archives for vcs products ... ")
430 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
435 logger.write("Done\n")
438 logger.write("Create the project ... ")
439 d_project = create_project_for_src_package(config,
442 logger.write("Done\n")
445 tmp_sat = add_salomeTools(config, tmp_working_dir)
446 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
448 # Add a sat symbolic link if not win
449 if not src.architecture.is_windows():
450 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
454 # In the jobs, os.getcwd() can fail
455 t = config.USER.workdir
456 os.chdir(tmp_working_dir)
457 if os.path.lexists(tmp_satlink_path):
458 os.remove(tmp_satlink_path)
459 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
462 d_sat["sat link"] = (tmp_satlink_path, "sat")
464 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
466 def get_archives(config, logger):
467 '''Find all the products that are get using an archive and all the products
468 that are get using a vcs (git, cvs, svn) repository.
470 :param config Config: The global configuration.
471 :param logger Logger: the logging instance
472 :return: the dictionary {name_product :
473 (local path of its archive, path in the package of its archive )}
474 and the list of specific configuration corresponding to the vcs
478 # Get the list of product informations
479 l_products_name = config.APPLICATION.products.keys()
480 l_product_info = src.product.get_products_infos(l_products_name,
484 for p_name, p_info in l_product_info:
485 # ignore the native and fixed products
486 if (src.product.product_is_native(p_info)
487 or src.product.product_is_fixed(p_info)):
489 if p_info.get_source == "archive":
490 archive_path = p_info.archive_info.archive_name
491 archive_name = os.path.basename(archive_path)
493 l_pinfo_vcs.append((p_name, p_info))
495 d_archives[p_name] = (archive_path,
496 os.path.join(ARCHIVE_DIR, archive_name))
497 return d_archives, l_pinfo_vcs
499 def add_salomeTools(config, tmp_working_dir):
500 '''Prepare a version of salomeTools that has a specific site.pyconf file
501 configured for a source package.
503 :param config Config: The global configuration.
504 :param tmp_working_dir str: The temporary local directory containing some
505 specific directories or files needed in the
507 :return: The path to the local salomeTools directory to add in the package
510 # Copy sat in the temporary working directory
511 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
512 sat_running_path = src.Path(config.VARS.salometoolsway)
513 sat_running_path.copy(sat_tmp_path)
515 # Update the site.pyconf file that contains the path to the project
516 site_pyconf_name = "site.pyconf"
517 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
518 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
519 ff = open(site_pyconf_file, "w")
520 ff.write(SITE_TEMPLATE)
523 return sat_tmp_path.path
525 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
526 '''For sources package that require that all products are get using an
527 archive, one has to create some archive for the vcs products.
528 So this method calls the clean and source command of sat and then create
531 :param l_pinfo_vcs List: The list of specific configuration corresponding to
533 :param sat Sat: The Sat instance that can be called to clean and source the
535 :param config Config: The global configuration.
536 :param logger Logger: the logging instance
537 :param tmp_working_dir str: The temporary local directory containing some
538 specific directories or files needed in the
540 :return: the dictionary that stores all the archives to add in the source
541 package. {label : (path_on_local_machine, path_in_archive)}
544 # clean the source directory of all the vcs products, then use the source
545 # command and thus construct an archive that will not contain the patches
546 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
548 logger.write(_("clean sources\n"))
549 args_clean = config.VARS.application
550 args_clean += " --sources --products "
551 args_clean += ",".join(l_prod_names)
552 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
554 logger.write(_("get sources"))
555 args_source = config.VARS.application
556 args_source += " --products "
557 args_source += ",".join(l_prod_names)
558 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
560 # make the new archives
562 for pn, pinfo in l_pinfo_vcs:
563 path_archive = make_archive(pn, pinfo, tmp_working_dir)
564 d_archives_vcs[pn] = (path_archive,
565 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
566 return d_archives_vcs
568 def make_archive(prod_name, prod_info, where):
569 '''Create an archive of a product by searching its source directory.
571 :param prod_name str: The name of the product.
572 :param prod_info Config: The specific configuration corresponding to the
574 :param where str: The path of the repository where to put the resulting
576 :return: The path of the resulting archive
579 path_targz_prod = os.path.join(where, prod_name + ".tgz")
580 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
581 local_path = prod_info.source_dir
582 tar_prod.add(local_path, arcname=prod_name)
584 return path_targz_prod
586 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
587 '''Create a specific project for a source package.
589 :param config Config: The global configuration.
590 :param tmp_working_dir str: The temporary local directory containing some
591 specific directories or files needed in the
593 :param with_vcs boolean: True if the package is with vcs products (not
594 transformed into archive products)
595 :return: The dictionary
596 {"project" : (produced project, project path in the archive)}
600 # Create in the working temporary directory the full project tree
601 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
602 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
604 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
607 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
610 patches_tmp_dir = os.path.join(project_tmp_dir,
613 application_tmp_dir = os.path.join(project_tmp_dir,
615 for directory in [project_tmp_dir,
616 compil_scripts_tmp_dir,
619 application_tmp_dir]:
620 src.ensure_path_exists(directory)
622 # Create the pyconf that contains the information of the project
623 project_pyconf_name = "project.pyconf"
624 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
625 ff = open(project_pyconf_file, "w")
626 ff.write(PROJECT_TEMPLATE)
629 # Loop over the products to get there pyconf and all the scripts
630 # (compilation, environment, patches)
631 # and create the pyconf file to add to the project
632 lproducts_name = config.APPLICATION.products.keys()
633 l_products = src.product.get_products_infos(lproducts_name, config)
634 for p_name, p_info in l_products:
635 # ignore native and fixed products
636 if (src.product.product_is_native(p_info) or
637 src.product.product_is_fixed(p_info)):
639 find_product_scripts_and_pyconf(p_name,
643 compil_scripts_tmp_dir,
646 products_pyconf_tmp_dir)
648 find_application_pyconf(config, application_tmp_dir)
650 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
653 def find_product_scripts_and_pyconf(p_name,
657 compil_scripts_tmp_dir,
660 products_pyconf_tmp_dir):
661 '''Create a specific pyconf file for a given product. Get its environment
662 script, its compilation script and patches and put it in the temporary
663 working directory. This method is used in the source package in order to
664 construct the specific project.
666 :param p_name str: The name of the product.
667 :param p_info Config: The specific configuration corresponding to the
669 :param config Config: The global configuration.
670 :param with_vcs boolean: True if the package is with vcs products (not
671 transformed into archive products)
672 :param compil_scripts_tmp_dir str: The path to the temporary compilation
673 scripts directory of the project.
674 :param env_scripts_tmp_dir str: The path to the temporary environment script
675 directory of the project.
676 :param patches_tmp_dir str: The path to the temporary patch scripts
677 directory of the project.
678 :param products_pyconf_tmp_dir str: The path to the temporary product
679 scripts directory of the project.
682 # read the pyconf of the product
683 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
684 config.PATHS.PRODUCTPATH)
685 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
687 # find the compilation script if any
688 if src.product.product_has_script(p_info):
689 compil_script_path = src.Path(p_info.compil_script)
690 compil_script_path.copy(compil_scripts_tmp_dir)
691 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
692 p_info.compil_script)
693 # find the environment script if any
694 if src.product.product_has_env_script(p_info):
695 env_script_path = src.Path(p_info.environ.env_script)
696 env_script_path.copy(env_scripts_tmp_dir)
697 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
698 p_info.environ.env_script)
699 # find the patches if any
700 if src.product.product_has_patches(p_info):
701 patches = src.pyconf.Sequence()
702 for patch_path in p_info.patches:
703 p_path = src.Path(patch_path)
704 p_path.copy(patches_tmp_dir)
705 patches.append(os.path.basename(patch_path), "")
707 product_pyconf_cfg[p_info.section].patches = patches
710 # put in the pyconf file the resolved values
711 for info in ["git_info", "cvs_info", "svn_info"]:
713 for key in p_info[info]:
714 product_pyconf_cfg[p_info.section][info][key] = p_info[
717 # if the product is not archive, then make it become archive.
718 if src.product.product_is_vcs(p_info):
719 product_pyconf_cfg[p_info.section].get_source = "archive"
720 if not "archive_info" in product_pyconf_cfg[p_info.section]:
721 product_pyconf_cfg[p_info.section].addMapping("archive_info",
722 src.pyconf.Mapping(product_pyconf_cfg),
724 product_pyconf_cfg[p_info.section
725 ].archive_info.archive_name = p_info.name + ".tgz"
727 # write the pyconf file to the temporary project location
728 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
730 ff = open(product_tmp_pyconf_path, 'w')
731 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
732 product_pyconf_cfg.__save__(ff, 1)
735 def find_application_pyconf(config, application_tmp_dir):
736 '''Find the application pyconf file and put it in the specific temporary
737 directory containing the specific project of a source package.
739 :param config Config: The global configuration.
740 :param application_tmp_dir str: The path to the temporary application
741 scripts directory of the project.
743 # read the pyconf of the application
744 application_name = config.VARS.application
745 application_pyconf_path = src.find_file_in_lpath(
746 application_name + ".pyconf",
747 config.PATHS.APPLICATIONPATH)
748 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
751 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
752 application_pyconf_cfg,
754 'VARS.salometoolsway + $VARS.sep + ".."')
756 # Prevent from compilation in base
757 application_pyconf_cfg.APPLICATION.no_base = "yes"
759 # write the pyconf file to the temporary application location
760 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
761 application_name + ".pyconf")
762 ff = open(application_tmp_pyconf_path, 'w')
763 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
764 application_pyconf_cfg.__save__(ff, 1)
767 def project_package(project_file_path, tmp_working_dir):
768 '''Prepare a dictionary that stores all the needed directories and files to
769 add in a project package.
771 :param project_file_path str: The path to the local project.
772 :param tmp_working_dir str: The temporary local directory containing some
773 specific directories or files needed in the
775 :return: the dictionary that stores all the needed directories and files to
776 add in a project package.
777 {label : (path_on_local_machine, path_in_archive)}
781 # Read the project file and get the directories to add to the package
782 project_pyconf_cfg = src.pyconf.Config(project_file_path)
783 paths = {"ARCHIVEPATH" : "archives",
784 "APPLICATIONPATH" : "applications",
785 "PRODUCTPATH" : "products",
787 "MACHINEPATH" : "machines"}
788 # Loop over the project paths and add it
790 if path not in project_pyconf_cfg:
792 # Add the directory to the files to add in the package
793 d_project[path] = (project_pyconf_cfg[path], paths[path])
794 # Modify the value of the path in the package
795 project_pyconf_cfg[path] = src.pyconf.Reference(
798 'project_path + "/' + paths[path] + '"')
801 if "project_path" not in project_pyconf_cfg:
802 project_pyconf_cfg.addMapping("project_path",
803 src.pyconf.Mapping(project_pyconf_cfg),
805 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
809 # Write the project pyconf file
810 project_file_name = os.path.basename(project_file_path)
811 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
812 ff = open(project_pyconf_tmp_path, 'w')
813 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
814 project_pyconf_cfg.__save__(ff, 1)
816 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
820 def add_readme(config, package_type, where):
821 readme_path = os.path.join(where, "README")
822 f = open(readme_path, 'w')
823 # prepare substitution dictionary
825 if package_type == BINARY:
826 d['application'] = config.VARS.application
827 d['user'] = config.VARS.user
828 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
829 d['version'] = config.INTERNAL.sat_version
830 d['dist'] = config.VARS.dist
831 if 'profile' in config.APPLICATION:
832 d['launcher'] = config.APPLICATION.profile.launcher_name
833 readme_template_path = os.path.join(config.VARS.internal_dir,
834 "README_BIN.template")
836 d['env_file'] = 'env_launch.sh'
837 readme_template_path = os.path.join(config.VARS.internal_dir,
838 "README_BIN_NO_PROFILE.template")
840 if package_type == SOURCE:
841 d['application'] = config.VARS.application
842 d['user'] = config.VARS.user
843 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
844 d['version'] = config.INTERNAL.sat_version
845 if 'profile' in config.APPLICATION:
846 d['profile'] = config.APPLICATION.profile.product
847 d['launcher'] = config.APPLICATION.profile.launcher_name
848 readme_template_path = os.path.join(config.VARS.internal_dir,
849 "README_SRC.template")
851 if package_type == PROJECT:
852 d['user'] = config.VARS.user
853 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
854 d['version'] = config.INTERNAL.sat_version
855 readme_template_path = os.path.join(config.VARS.internal_dir,
856 "README_PROJECT.template")
858 if package_type == SAT:
859 d['user'] = config.VARS.user
860 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
861 d['version'] = config.INTERNAL.sat_version
862 readme_template_path = os.path.join(config.VARS.internal_dir,
863 "README_SAT.template")
865 f.write(src.template.substitute(readme_template_path, d))
871 '''method that is called when salomeTools is called with --help option.
873 :return: The text to display for the package command description.
876 return _("The package command creates an archive.\nThere are 4 kinds of "
877 "archive:\n 1- The binary archive. It contains all the product "
878 "installation directories and a launcher,\n 2- The sources archive."
879 " It contains the products archives, a project corresponding to "
880 "the application and salomeTools,\n 3- The project archive. It "
881 "contains a project (give the project file path as argument),\n 4-"
882 " The salomeTools archive. It contains salomeTools.\n\nexample:"
883 "\nsat package SALOME-master --sources")
885 def run(args, runner, logger):
886 '''method that is called when salomeTools is called with package parameter.
890 (options, args) = parser.parse_args(args)
892 # Check that a type of package is called, and only one
893 all_option_types = (options.binaries,
895 options.project not in ["", None],
898 # Check if no option for package type
899 if all_option_types.count(True) == 0:
900 msg = _("Error: Precise a type for the package\nUse one of the "
901 "following options: --binaries, --sources, --project or"
903 logger.write(src.printcolors.printcError(msg), 1)
904 logger.write("\n", 1)
907 # Check for only one option for package type
908 if all_option_types.count(True) > 1:
909 msg = _("Error: You can use only one type for the package\nUse only one"
910 " of the following options: --binaries, --sources, --project or"
912 logger.write(src.printcolors.printcError(msg), 1)
913 logger.write("\n", 1)
916 # Get the package type
918 package_type = BINARY
920 package_type = SOURCE
922 package_type = PROJECT
926 # The repository where to put the package if not Binary or Source
927 package_default_path = runner.cfg.USER.workdir
929 if package_type in [BINARY, SOURCE]:
930 # Check that the command has been called with an application
931 src.check_config_has_application(runner.cfg)
933 # Display information
934 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
935 runner.cfg.VARS.application), 1)
937 # Get the default directory where to put the packages
938 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
940 src.ensure_path_exists(package_default_path)
942 elif package_type == PROJECT:
943 # check that the project is visible by SAT
944 if options.project not in runner.cfg.PROJECTS.project_file_paths:
945 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
948 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
949 "\nPlease add it in the %(site)s file." % {
950 "proj" : options.project, "site" : site_path})
951 logger.write(src.printcolors.printcError(msg), 1)
952 logger.write("\n", 1)
956 src.printcolors.print_value(logger, "Package type", package_type, 2)
958 # get the name of the archive or construct it
960 if os.path.basename(options.name) == options.name:
961 # only a name (not a path)
962 archive_name = options.name
963 dir_name = package_default_path
965 archive_name = os.path.basename(options.name)
966 dir_name = os.path.dirname(options.name)
969 if archive_name[-len(".tgz"):] == ".tgz":
970 archive_name = archive_name[:-len(".tgz")]
971 if archive_name[-len(".tar.gz"):] == ".tar.gz":
972 archive_name = archive_name[:-len(".tar.gz")]
975 dir_name = package_default_path
976 if package_type == BINARY:
977 archive_name = (runner.cfg.APPLICATION.name +
979 runner.cfg.VARS.dist)
981 if package_type == SOURCE:
982 archive_name = (runner.cfg.APPLICATION.name +
986 archive_name = (runner.cfg.APPLICATION.name +
992 if package_type == PROJECT:
993 project_name, __ = os.path.splitext(
994 os.path.basename(options.project))
995 archive_name = ("PROJECT" +
999 if package_type == SAT:
1000 archive_name = ("salomeTools" +
1002 runner.cfg.INTERNAL.sat_version)
1004 path_targz = os.path.join(dir_name, archive_name + ".tgz")
1006 # Print the path of the package
1007 src.printcolors.print_value(logger, "Package path", path_targz, 2)
1009 # Create a working directory for all files that are produced during the
1010 # package creation and that will be removed at the end of the command
1011 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
1012 runner.cfg.VARS.datehour)
1013 src.ensure_path_exists(tmp_working_dir)
1014 logger.write("\n", 5)
1015 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
1017 logger.write("\n", 3)
1019 msg = _("Preparation of files to add to the archive")
1020 logger.write(src.printcolors.printcLabel(msg), 2)
1021 logger.write("\n", 2)
1023 if package_type == BINARY:
1024 d_files_to_add = binary_package(runner.cfg,
1028 if not(d_files_to_add):
1031 if package_type == SOURCE:
1032 d_files_to_add = source_package(runner,
1038 if package_type == PROJECT:
1039 d_files_to_add = project_package(options.project, tmp_working_dir)
1041 if package_type == SAT:
1042 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
1044 # Add the README file in the package
1045 local_readme_tmp_path = add_readme(runner.cfg,
1048 d_files_to_add["README"] = (local_readme_tmp_path, "README")
1050 # Add the additional files of option add_files
1051 if options.add_files:
1052 for file_path in options.add_files:
1053 if not os.path.exists(file_path):
1054 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1056 file_name = os.path.basename(file_path)
1057 d_files_to_add[file_name] = (file_path, file_name)
1059 logger.write("\n", 2)
1061 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1062 logger.write("\n", 2)
1065 # Creating the object tarfile
1066 tar = tarfile.open(path_targz, mode='w:gz')
1068 # Add the files to the tarfile object
1069 res = add_files(tar, archive_name, d_files_to_add, logger)
1071 except KeyboardInterrupt:
1072 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1073 logger.write(_("Removing the temporary working directory ... "), 1)
1074 # remove the working directory
1075 shutil.rmtree(tmp_working_dir)
1076 logger.write(_("OK"), 1)
1077 logger.write(_("\n"), 1)
1080 # remove the working directory
1081 shutil.rmtree(tmp_working_dir)
1083 # Print again the path of the package
1084 logger.write("\n", 2)
1085 src.printcolors.print_value(logger, "Package path", path_targz, 2)