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
32 ARCHIVE_DIR = "ARCHIVES"
33 PROJECT_DIR = "PROJECT"
35 PROJECT_TEMPLATE = """#!/usr/bin/env python
38 # The path to the archive root directory
39 root_path : $PWD + "/../"
41 project_path : $PWD + "/"
43 # Where to search the archives of the products
44 ARCHIVEPATH : $root_path + "ARCHIVES"
45 # Where to search the pyconf of the applications
46 APPLICATIONPATH : $project_path + "applications/"
47 # Where to search the pyconf of the products
48 PRODUCTPATH : $project_path + "products/"
49 # Where to search the pyconf of the jobs of the project
50 JOBPATH : $project_path + "jobs/"
51 # Where to search the pyconf of the machines of the project
52 MACHINEPATH : $project_path + "machines/"
55 SITE_TEMPLATE = ("""#!/usr/bin/env python
62 log_dir : $USER.workdir + "/LOGS"
65 tmp_dir_with_application : '/tmp' + $VARS.sep + $VARS.user + """
66 """$VARS.sep + $APPLICATION.name + $VARS.sep + 'test'
67 tmp_dir : '/tmp' + $VARS.sep + $VARS.user + $VARS.sep + 'test'
74 project_file_paths : [$VARS.salometoolsway + $VARS.sep + \"..\" + $VARS.sep"""
75 """ + \"""" + PROJECT_DIR + """\" + $VARS.sep + "project.pyconf"]
79 # Define all possible option for the package command : sat package <options>
80 parser = src.options.Options()
81 parser.add_option('b', 'binaries', 'boolean', 'binaries',
82 _('Optional: Produce a binary package.'), False)
83 parser.add_option('f', 'force_creation', 'boolean', 'force_creation',
84 _('Optional: Only binary package: produce the archive even if '
85 'there are some missing products.'), False)
86 parser.add_option('s', 'sources', 'boolean', 'sources',
87 _('Optional: Produce a compilable archive of the sources of the '
88 'application.'), False)
89 parser.add_option('', 'with_vcs', 'boolean', 'with_vcs',
90 _('Optional: Only source package: do not make archive of vcs products.'),
92 parser.add_option('p', 'project', 'string', 'project',
93 _('Optional: Produce an archive that contains a project.'), "")
94 parser.add_option('t', 'salometools', 'boolean', 'sat',
95 _('Optional: Produce an archive that contains salomeTools.'), False)
96 parser.add_option('n', 'name', 'string', 'name',
97 _('Optional: The name or full path of the archive.'), None)
98 parser.add_option('', 'add_files', 'list2', 'add_files',
99 _('Optional: The list of additional files to add to the archive.'), [])
100 parser.add_option('', 'without_commercial', 'boolean', 'without_commercial',
101 _('Optional: do not add commercial licence.'), False)
103 def add_files(tar, name_archive, d_content, logger):
104 '''Create an archive containing all directories and files that are given in
105 the d_content argument.
107 :param tar tarfile: The tarfile instance used to make the archive.
108 :param name_archive str: The name of the archive to make.
109 :param d_content dict: The dictionary that contain all directories and files
110 to add in the archive.
112 (path_on_local_machine, path_in_archive)
113 :param logger Logger: the logging instance
114 :return: 0 if success, 1 if not.
117 # get the max length of the messages in order to make the display
118 max_len = len(max(d_content.keys(), key=len))
121 # loop over each directory or file stored in the d_content dictionary
122 for name in d_content.keys():
123 # display information
124 len_points = max_len - len(name)
125 logger.write(name + " " + len_points * "." + " ", 3)
126 # Get the local path and the path in archive
127 # of the directory or file to add
128 local_path, archive_path = d_content[name]
129 in_archive = os.path.join(name_archive, archive_path)
130 # Add it in the archive
132 tar.add(local_path, arcname=in_archive)
133 logger.write(src.printcolors.printcSuccess(_("OK")), 3)
134 except Exception as e:
135 logger.write(src.printcolors.printcError(_("KO ")), 3)
136 logger.write(str(e), 3)
138 logger.write("\n", 3)
141 def produce_relative_launcher(config,
146 with_commercial=True):
147 '''Create a specific SALOME launcher for the binary package. This launcher
150 :param config Config: The global configuration.
151 :param logger Logger: the logging instance
152 :param file_dir str: the directory where to put the launcher
153 :param file_name str: The launcher name
154 :param binaries_dir_name str: the name of the repository where the binaries
156 :return: the path of the produced launcher
160 # Get the launcher template
161 profile_install_dir = os.path.join(binaries_dir_name,
162 config.APPLICATION.profile.product)
163 withProfile = src.fileEnviron.withProfile
164 withProfile = withProfile.replace(
165 "ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR'",
166 "ABSOLUTE_APPLI_PATH'] = out_dir_Path + '" + config.VARS.sep + profile_install_dir + "'")
167 withProfile = withProfile.replace(
168 "os.path.join( 'PROFILE_INSTALL_DIR'",
169 "os.path.join( out_dir_Path, '" + profile_install_dir + "'")
171 before, after = withProfile.split(
172 "# here your local standalone environment\n")
174 # create an environment file writer
175 writer = src.environment.FileEnvWriter(config,
180 filepath = os.path.join(file_dir, file_name)
181 # open the file and write into it
182 launch_file = open(filepath, "w")
183 launch_file.write(before)
185 writer.write_cfgForPy_file(launch_file,
186 for_package = binaries_dir_name,
187 with_commercial=with_commercial)
188 launch_file.write(after)
191 # Little hack to put out_dir_Path outside the strings
192 src.replace_in_file(filepath, 'r"out_dir_Path', 'out_dir_Path + r"' )
194 # change the rights in order to make the file executable for everybody
206 def produce_relative_env_files(config,
210 '''Create some specific environment files for the binary package. These
211 files use relative paths.
213 :param config Config: The global configuration.
214 :param logger Logger: the logging instance
215 :param file_dir str: the directory where to put the files
216 :param binaries_dir_name str: the name of the repository where the binaries
218 :return: the list of path of the produced environment files
221 # create an environment file writer
222 writer = src.environment.FileEnvWriter(config,
228 filepath = writer.write_env_file("env_launch.sh",
231 for_package = binaries_dir_name)
233 # Little hack to put out_dir_Path as environment variable
234 src.replace_in_file(filepath, '"out_dir_Path', '"${out_dir_Path}' )
236 # change the rights in order to make the file executable for everybody
248 def product_appli_creation_script(config,
252 '''Create a script that can produce an application (EDF style) in the binary
255 :param config Config: The global configuration.
256 :param logger Logger: the logging instance
257 :param file_dir str: the directory where to put the file
258 :param binaries_dir_name str: the name of the repository where the binaries
260 :return: the path of the produced script file
263 template_name = "create_appli.py.for_bin_packages.template"
264 template_path = os.path.join(config.VARS.internal_dir, template_name)
265 text_to_fill = open(template_path, "r").read()
266 text_to_fill = text_to_fill.replace("TO BE FILLED 1",
267 '"' + binaries_dir_name + '"')
270 for product_name in config.APPLICATION.products:
271 product_info = src.product.get_product_config(config, product_name)
272 if src.product.product_is_SALOME(product_info):
273 line_to_add = ("<module name=\"" +
275 "\" gui=\"yes\" path=\"''' + "
276 "os.path.join(dir_bin_name, \"" +
277 product_name + "\") + '''\"/>")
278 text_to_add += line_to_add + "\n"
280 filled_text = text_to_fill.replace("TO BE FILLED 2", text_to_add)
282 tmp_file_path = os.path.join(file_dir, "create_appli.py")
283 ff = open(tmp_file_path, "w")
284 ff.write(filled_text)
287 # change the rights in order to make the file executable for everybody
288 os.chmod(tmp_file_path,
299 def binary_package(config, logger, options, tmp_working_dir):
300 '''Prepare a dictionary that stores all the needed directories and files to
301 add in a binary package.
303 :param config Config: The global configuration.
304 :param logger Logger: the logging instance
305 :param options OptResult: the options of the launched command
306 :param tmp_working_dir str: The temporary local directory containing some
307 specific directories or files needed in the
309 :return: the dictionary that stores all the needed directories and files to
310 add in a binary package.
311 {label : (path_on_local_machine, path_in_archive)}
315 # Get the list of product installation to add to the archive
316 l_products_name = config.APPLICATION.products.keys()
317 l_product_info = src.product.get_products_infos(l_products_name,
321 for prod_name, prod_info in l_product_info:
322 # ignore the native and fixed products
323 if (src.product.product_is_native(prod_info)
324 or src.product.product_is_fixed(prod_info)
325 or not src.product.product_compiles(prod_info)):
327 if src.product.check_installation(prod_info):
328 l_install_dir.append((prod_name, prod_info.install_dir))
330 l_not_installed.append(prod_name)
332 # Print warning or error if there are some missing products
333 if len(l_not_installed) > 0:
334 text_missing_prods = ""
335 for p_name in l_not_installed:
336 text_missing_prods += "-" + p_name + "\n"
337 if not options.force_creation:
338 msg = _("ERROR: there are missing products installations:")
339 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
344 msg = _("WARNING: there are missing products installations:")
345 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
349 # construct the name of the directory that will contain the binaries
350 binaries_dir_name = "BINARIES-" + config.VARS.dist
352 # construct the correlation table between the product names, there
353 # actual install directories and there install directory in archive
355 for prod_name, install_dir in l_install_dir:
356 path_in_archive = os.path.join(binaries_dir_name, prod_name)
357 d_products[prod_name] = (install_dir, path_in_archive)
359 # create the relative launcher and add it to the files to add
360 if "profile" in config.APPLICATION:
361 launcher_name = config.APPLICATION.profile.launcher_name
362 launcher_package = produce_relative_launcher(config,
367 not(options.without_commercial))
369 d_products["launcher"] = (launcher_package, launcher_name)
371 # No profile, it means that there has to be some environment files
372 env_file = produce_relative_env_files(config,
377 d_products["environment file"] = (env_file, "env_launch.sh")
379 # And provide a script for the creation of an application EDF style
380 appli_script = product_appli_creation_script(config,
385 d_products["appli script"] = (appli_script, "create_appli.py")
389 def source_package(sat, config, logger, options, tmp_working_dir):
390 '''Prepare a dictionary that stores all the needed directories and files to
391 add in a source package.
393 :param config Config: The global configuration.
394 :param logger Logger: the logging instance
395 :param options OptResult: the options of the launched command
396 :param tmp_working_dir str: The temporary local directory containing some
397 specific directories or files needed in the
399 :return: the dictionary that stores all the needed directories and files to
400 add in a source package.
401 {label : (path_on_local_machine, path_in_archive)}
405 # Get all the products that are prepared using an archive
406 logger.write("Find archive products ... ")
407 d_archives, l_pinfo_vcs = get_archives(config, logger)
408 logger.write("Done\n")
410 if not options.with_vcs and len(l_pinfo_vcs) > 0:
411 # Make archives with the products that are not prepared using an archive
412 # (git, cvs, svn, etc)
413 logger.write("Construct archives for vcs products ... ")
414 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
419 logger.write("Done\n")
422 logger.write("Create the project ... ")
423 d_project = create_project_for_src_package(config,
426 logger.write("Done\n")
429 tmp_sat = add_salomeTools(config, tmp_working_dir)
430 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
432 # Add a sat symbolic link if not win
433 if not src.architecture.is_windows():
434 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
438 # In the jobs, os.getcwd() can fail
439 t = config.USER.workdir
440 os.chdir(tmp_working_dir)
441 if os.path.lexists(tmp_satlink_path):
442 os.remove(tmp_satlink_path)
443 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
446 d_sat["sat link"] = (tmp_satlink_path, "sat")
448 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
450 def get_archives(config, logger):
451 '''Find all the products that are get using an archive and all the products
452 that are get using a vcs (git, cvs, svn) repository.
454 :param config Config: The global configuration.
455 :param logger Logger: the logging instance
456 :return: the dictionary {name_product :
457 (local path of its archive, path in the package of its archive )}
458 and the list of specific configuration corresponding to the vcs
462 # Get the list of product informations
463 l_products_name = config.APPLICATION.products.keys()
464 l_product_info = src.product.get_products_infos(l_products_name,
468 for p_name, p_info in l_product_info:
469 # ignore the native and fixed products
470 if (src.product.product_is_native(p_info)
471 or src.product.product_is_fixed(p_info)):
473 if p_info.get_source == "archive":
474 archive_path = p_info.archive_info.archive_name
475 archive_name = os.path.basename(archive_path)
477 l_pinfo_vcs.append((p_name, p_info))
479 d_archives[p_name] = (archive_path,
480 os.path.join(ARCHIVE_DIR, archive_name))
481 return d_archives, l_pinfo_vcs
483 def add_salomeTools(config, tmp_working_dir):
484 '''Prepare a version of salomeTools that has a specific site.pyconf file
485 configured for a source package.
487 :param config Config: The global configuration.
488 :param tmp_working_dir str: The temporary local directory containing some
489 specific directories or files needed in the
491 :return: The path to the local salomeTools directory to add in the package
494 # Copy sat in the temporary working directory
495 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
496 sat_running_path = src.Path(config.VARS.salometoolsway)
497 sat_running_path.copy(sat_tmp_path)
499 # Update the site.pyconf file that contains the path to the project
500 site_pyconf_name = "site.pyconf"
501 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
502 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
503 ff = open(site_pyconf_file, "w")
504 ff.write(SITE_TEMPLATE)
507 return sat_tmp_path.path
509 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
510 '''For sources package that require that all products are get using an
511 archive, one has to create some archive for the vcs products.
512 So this method calls the clean and source command of sat and then create
515 :param l_pinfo_vcs List: The list of specific configuration corresponding to
517 :param sat Sat: The Sat instance that can be called to clean and source the
519 :param config Config: The global configuration.
520 :param logger Logger: the logging instance
521 :param tmp_working_dir str: The temporary local directory containing some
522 specific directories or files needed in the
524 :return: the dictionary that stores all the archives to add in the source
525 package. {label : (path_on_local_machine, path_in_archive)}
528 # clean the source directory of all the vcs products, then use the source
529 # command and thus construct an archive that will not contain the patches
530 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
532 logger.write(_("clean sources\n"))
533 args_clean = config.VARS.application
534 args_clean += " --sources --products "
535 args_clean += ",".join(l_prod_names)
536 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
538 logger.write(_("get sources"))
539 args_source = config.VARS.application
540 args_source += " --products "
541 args_source += ",".join(l_prod_names)
542 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
544 # make the new archives
546 for pn, pinfo in l_pinfo_vcs:
547 path_archive = make_archive(pn, pinfo, tmp_working_dir)
548 d_archives_vcs[pn] = (path_archive,
549 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
550 return d_archives_vcs
552 def make_archive(prod_name, prod_info, where):
553 '''Create an archive of a product by searching its source directory.
555 :param prod_name str: The name of the product.
556 :param prod_info Config: The specific configuration corresponding to the
558 :param where str: The path of the repository where to put the resulting
560 :return: The path of the resulting archive
563 path_targz_prod = os.path.join(where, prod_name + ".tgz")
564 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
565 local_path = prod_info.source_dir
566 tar_prod.add(local_path, arcname=prod_name)
568 return path_targz_prod
570 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
571 '''Create a specific project for a source package.
573 :param config Config: The global configuration.
574 :param tmp_working_dir str: The temporary local directory containing some
575 specific directories or files needed in the
577 :param with_vcs boolean: True if the package is with vcs products (not
578 transformed into archive products)
579 :return: The dictionary
580 {"project" : (produced project, project path in the archive)}
584 # Create in the working temporary directory the full project tree
585 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
586 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
588 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
591 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
594 patches_tmp_dir = os.path.join(project_tmp_dir,
597 application_tmp_dir = os.path.join(project_tmp_dir,
599 for directory in [project_tmp_dir,
600 compil_scripts_tmp_dir,
603 application_tmp_dir]:
604 src.ensure_path_exists(directory)
606 # Create the pyconf that contains the information of the project
607 project_pyconf_name = "project.pyconf"
608 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
609 ff = open(project_pyconf_file, "w")
610 ff.write(PROJECT_TEMPLATE)
613 # Loop over the products to get there pyconf and all the scripts
614 # (compilation, environment, patches)
615 # and create the pyconf file to add to the project
616 lproducts_name = config.APPLICATION.products.keys()
617 l_products = src.product.get_products_infos(lproducts_name, config)
618 for p_name, p_info in l_products:
619 # ignore native and fixed products
620 if (src.product.product_is_native(p_info) or
621 src.product.product_is_fixed(p_info)):
623 find_product_scripts_and_pyconf(p_name,
627 compil_scripts_tmp_dir,
630 products_pyconf_tmp_dir)
632 find_application_pyconf(config, application_tmp_dir)
634 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
637 def find_product_scripts_and_pyconf(p_name,
641 compil_scripts_tmp_dir,
644 products_pyconf_tmp_dir):
645 '''Create a specific pyconf file for a given product. Get its environment
646 script, its compilation script and patches and put it in the temporary
647 working directory. This method is used in the source package in order to
648 construct the specific project.
650 :param p_name str: The name of the product.
651 :param p_info Config: The specific configuration corresponding to the
653 :param config Config: The global configuration.
654 :param with_vcs boolean: True if the package is with vcs products (not
655 transformed into archive products)
656 :param compil_scripts_tmp_dir str: The path to the temporary compilation
657 scripts directory of the project.
658 :param env_scripts_tmp_dir str: The path to the temporary environment script
659 directory of the project.
660 :param patches_tmp_dir str: The path to the temporary patch scripts
661 directory of the project.
662 :param products_pyconf_tmp_dir str: The path to the temporary product
663 scripts directory of the project.
666 # read the pyconf of the product
667 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
668 config.PATHS.PRODUCTPATH)
669 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
671 # find the compilation script if any
672 if src.product.product_has_script(p_info):
673 compil_script_path = src.Path(p_info.compil_script)
674 compil_script_path.copy(compil_scripts_tmp_dir)
675 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
676 p_info.compil_script)
677 # find the environment script if any
678 if src.product.product_has_env_script(p_info):
679 env_script_path = src.Path(p_info.environ.env_script)
680 env_script_path.copy(env_scripts_tmp_dir)
681 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
682 p_info.environ.env_script)
683 # find the patches if any
684 if src.product.product_has_patches(p_info):
685 patches = src.pyconf.Sequence()
686 for patch_path in p_info.patches:
687 p_path = src.Path(patch_path)
688 p_path.copy(patches_tmp_dir)
689 patches.append(os.path.basename(patch_path), "")
691 product_pyconf_cfg[p_info.section].patches = patches
694 # put in the pyconf file the resolved values
695 for info in ["git_info", "cvs_info", "svn_info"]:
697 for key in p_info[info]:
698 product_pyconf_cfg[p_info.section][info][key] = p_info[
701 # if the product is not archive, then make it become archive.
702 if src.product.product_is_vcs(p_info):
703 product_pyconf_cfg[p_info.section].get_source = "archive"
704 if not "archive_info" in product_pyconf_cfg[p_info.section]:
705 product_pyconf_cfg[p_info.section].addMapping("archive_info",
706 src.pyconf.Mapping(product_pyconf_cfg),
708 product_pyconf_cfg[p_info.section
709 ].archive_info.archive_name = p_info.name + ".tgz"
711 # write the pyconf file to the temporary project location
712 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
714 ff = open(product_tmp_pyconf_path, 'w')
715 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
716 product_pyconf_cfg.__save__(ff, 1)
719 def find_application_pyconf(config, application_tmp_dir):
720 '''Find the application pyconf file and put it in the specific temporary
721 directory containing the specific project of a source package.
723 :param config Config: The global configuration.
724 :param application_tmp_dir str: The path to the temporary application
725 scripts directory of the project.
727 # read the pyconf of the application
728 application_name = config.VARS.application
729 application_pyconf_path = src.find_file_in_lpath(
730 application_name + ".pyconf",
731 config.PATHS.APPLICATIONPATH)
732 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
735 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
736 application_pyconf_cfg,
738 'VARS.salometoolsway + $VARS.sep + ".."')
740 # Prevent from compilation in base
741 application_pyconf_cfg.APPLICATION.no_base = "yes"
743 # write the pyconf file to the temporary application location
744 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
745 application_name + ".pyconf")
746 ff = open(application_tmp_pyconf_path, 'w')
747 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
748 application_pyconf_cfg.__save__(ff, 1)
751 def project_package(project_file_path, tmp_working_dir):
752 '''Prepare a dictionary that stores all the needed directories and files to
753 add in a project package.
755 :param project_file_path str: The path to the local project.
756 :param tmp_working_dir str: The temporary local directory containing some
757 specific directories or files needed in the
759 :return: the dictionary that stores all the needed directories and files to
760 add in a project package.
761 {label : (path_on_local_machine, path_in_archive)}
765 # Read the project file and get the directories to add to the package
766 project_pyconf_cfg = src.pyconf.Config(project_file_path)
767 paths = {"ARCHIVEPATH" : "archives",
768 "APPLICATIONPATH" : "applications",
769 "PRODUCTPATH" : "products",
771 "MACHINEPATH" : "machines"}
772 # Loop over the project paths and add it
774 if path not in project_pyconf_cfg:
776 # Add the directory to the files to add in the package
777 d_project[path] = (project_pyconf_cfg[path], paths[path])
778 # Modify the value of the path in the package
779 project_pyconf_cfg[path] = src.pyconf.Reference(
782 'project_path + "/' + paths[path] + '"')
785 if "project_path" not in project_pyconf_cfg:
786 project_pyconf_cfg.addMapping("project_path",
787 src.pyconf.Mapping(project_pyconf_cfg),
789 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
793 # Write the project pyconf file
794 project_file_name = os.path.basename(project_file_path)
795 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
796 ff = open(project_pyconf_tmp_path, 'w')
797 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
798 project_pyconf_cfg.__save__(ff, 1)
800 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
804 def add_readme(config, package_type, where):
805 readme_path = os.path.join(where, "README")
806 f = open(readme_path, 'w')
807 # prepare substitution dictionary
809 if package_type == BINARY:
810 d['application'] = config.VARS.application
811 d['user'] = config.VARS.user
812 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
813 d['version'] = config.INTERNAL.sat_version
814 d['dist'] = config.VARS.dist
815 if 'profile' in config.APPLICATION:
816 d['launcher'] = config.APPLICATION.profile.launcher_name
817 readme_template_path = os.path.join(config.VARS.internal_dir,
818 "README_BIN.template")
820 d['env_file'] = 'env_launch.sh'
821 readme_template_path = os.path.join(config.VARS.internal_dir,
822 "README_BIN_NO_PROFILE.template")
824 if package_type == SOURCE:
825 d['application'] = config.VARS.application
826 d['user'] = config.VARS.user
827 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
828 d['version'] = config.INTERNAL.sat_version
829 if 'profile' in config.APPLICATION:
830 d['profile'] = config.APPLICATION.profile.product
831 d['launcher'] = config.APPLICATION.profile.launcher_name
832 readme_template_path = os.path.join(config.VARS.internal_dir,
833 "README_SRC.template")
835 if package_type == PROJECT:
836 d['user'] = config.VARS.user
837 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
838 d['version'] = config.INTERNAL.sat_version
839 readme_template_path = os.path.join(config.VARS.internal_dir,
840 "README_PROJECT.template")
842 if package_type == SAT:
843 d['user'] = config.VARS.user
844 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
845 d['version'] = config.INTERNAL.sat_version
846 readme_template_path = os.path.join(config.VARS.internal_dir,
847 "README_SAT.template")
849 f.write(src.template.substitute(readme_template_path, d))
855 '''method that is called when salomeTools is called with --help option.
857 :return: The text to display for the package command description.
860 return _("The package command creates an archive.\nThere are 4 kinds of "
861 "archive:\n 1- The binary archive. It contains all the product "
862 "installation directories and a launcher,\n 2- The sources archive."
863 " It contains the products archives, a project corresponding to "
864 "the application and salomeTools,\n 3- The project archive. It "
865 "contains a project (give the project file path as argument),\n 4-"
866 " The salomeTools archive. It contains salomeTools.\n\nexample:"
867 "\nsat package SALOME-master --sources")
869 def run(args, runner, logger):
870 '''method that is called when salomeTools is called with package parameter.
874 (options, args) = parser.parse_args(args)
876 # Check that a type of package is called, and only one
877 all_option_types = (options.binaries,
879 options.project not in ["", None],
882 # Check if no option for package type
883 if all_option_types.count(True) == 0:
884 msg = _("Error: Precise a type for the package\nUse one of the "
885 "following options: --binaries, --sources, --project or"
887 logger.write(src.printcolors.printcError(msg), 1)
888 logger.write("\n", 1)
891 # Check for only one option for package type
892 if all_option_types.count(True) > 1:
893 msg = _("Error: You can use only one type for the package\nUse only one"
894 " of the following options: --binaries, --sources, --project or"
896 logger.write(src.printcolors.printcError(msg), 1)
897 logger.write("\n", 1)
900 # Get the package type
902 package_type = BINARY
904 package_type = SOURCE
906 package_type = PROJECT
910 # The repository where to put the package if not Binary or Source
911 package_default_path = runner.cfg.USER.workdir
913 if package_type in [BINARY, SOURCE]:
914 # Check that the command has been called with an application
915 src.check_config_has_application(runner.cfg)
917 # Display information
918 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
919 runner.cfg.VARS.application), 1)
921 # Get the default directory where to put the packages
922 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
924 src.ensure_path_exists(package_default_path)
926 elif package_type == PROJECT:
927 # check that the project is visible by SAT
928 if options.project not in runner.cfg.PROJECTS.project_file_paths:
929 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
932 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
933 "\nPlease add it in the %(site)s file." % {
934 "proj" : options.project, "site" : site_path})
935 logger.write(src.printcolors.printcError(msg), 1)
936 logger.write("\n", 1)
940 src.printcolors.print_value(logger, "Package type", package_type, 2)
942 # get the name of the archive or construct it
944 if os.path.basename(options.name) == options.name:
945 # only a name (not a path)
946 archive_name = options.name
947 dir_name = package_default_path
949 archive_name = os.path.basename(options.name)
950 dir_name = os.path.dirname(options.name)
953 if archive_name[-len(".tgz"):] == ".tgz":
954 archive_name = archive_name[:-len(".tgz")]
955 if archive_name[-len(".tar.gz"):] == ".tar.gz":
956 archive_name = archive_name[:-len(".tar.gz")]
959 dir_name = package_default_path
960 if package_type == BINARY:
961 archive_name = (runner.cfg.APPLICATION.name +
963 runner.cfg.VARS.dist)
965 if package_type == SOURCE:
966 archive_name = (runner.cfg.APPLICATION.name +
970 archive_name = (runner.cfg.APPLICATION.name +
976 if package_type == PROJECT:
977 project_name, __ = os.path.splitext(
978 os.path.basename(options.project))
979 archive_name = ("PROJECT" +
983 if package_type == SAT:
984 archive_name = ("salomeTools" +
986 runner.cfg.INTERNAL.sat_version)
988 path_targz = os.path.join(dir_name, archive_name + ".tgz")
990 # Print the path of the package
991 src.printcolors.print_value(logger, "Package path", path_targz, 2)
993 # Create a working directory for all files that are produced during the
994 # package creation and that will be removed at the end of the command
995 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
996 runner.cfg.VARS.datehour)
997 src.ensure_path_exists(tmp_working_dir)
998 logger.write("\n", 5)
999 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
1001 logger.write("\n", 3)
1003 msg = _("Preparation of files to add to the archive")
1004 logger.write(src.printcolors.printcLabel(msg), 2)
1005 logger.write("\n", 2)
1007 if package_type == BINARY:
1008 d_files_to_add = binary_package(runner.cfg,
1012 if not(d_files_to_add):
1015 if package_type == SOURCE:
1016 d_files_to_add = source_package(runner,
1022 if package_type == PROJECT:
1023 d_files_to_add = project_package(options.project, tmp_working_dir)
1025 if package_type == SAT:
1026 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
1028 # Add the README file in the package
1029 local_readme_tmp_path = add_readme(runner.cfg,
1032 d_files_to_add["README"] = (local_readme_tmp_path, "README")
1034 # Add the additional files of option add_files
1035 if options.add_files:
1036 for file_path in options.add_files:
1037 if not os.path.exists(file_path):
1038 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1040 file_name = os.path.basename(file_path)
1041 d_files_to_add[file_name] = (file_path, file_name)
1043 logger.write("\n", 2)
1045 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1046 logger.write("\n", 2)
1049 # Creating the object tarfile
1050 tar = tarfile.open(path_targz, mode='w:gz')
1052 # Add the files to the tarfile object
1053 res = add_files(tar, archive_name, d_files_to_add, logger)
1055 except KeyboardInterrupt:
1056 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1057 logger.write(_("Removing the temporary working directory ... "), 1)
1058 # remove the working directory
1059 shutil.rmtree(tmp_working_dir)
1060 logger.write(_("OK"), 1)
1061 logger.write(_("\n"), 1)
1064 # remove the working directory
1065 shutil.rmtree(tmp_working_dir)
1067 # Print again the path of the package
1068 logger.write("\n", 2)
1069 src.printcolors.print_value(logger, "Package path", path_targz, 2)