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)
289 def binary_package(config, logger, options, tmp_working_dir):
290 '''Prepare a dictionary that stores all the needed directories and files to
291 add in a binary package.
293 :param config Config: The global configuration.
294 :param logger Logger: the logging instance
295 :param options OptResult: the options of the launched command
296 :param tmp_working_dir str: The temporary local directory containing some
297 specific directories or files needed in the
299 :return: the dictionary that stores all the needed directories and files to
300 add in a binary package.
301 {label : (path_on_local_machine, path_in_archive)}
305 # Get the list of product installation to add to the archive
306 l_products_name = config.APPLICATION.products.keys()
307 l_product_info = src.product.get_products_infos(l_products_name,
311 for prod_name, prod_info in l_product_info:
312 # ignore the native and fixed products
313 if (src.product.product_is_native(prod_info)
314 or src.product.product_is_fixed(prod_info)
315 or not src.product.product_compiles(prod_info)):
317 if src.product.check_installation(prod_info):
318 l_install_dir.append((prod_name, prod_info.install_dir))
320 l_not_installed.append(prod_name)
322 # Print warning or error if there are some missing products
323 if len(l_not_installed) > 0:
324 text_missing_prods = ""
325 for p_name in l_not_installed:
326 text_missing_prods += "-" + p_name + "\n"
327 if not options.force_creation:
328 msg = _("ERROR: there are missing products installations:")
329 logger.write("%s\n%s" % (src.printcolors.printcError(msg),
334 msg = _("WARNING: there are missing products installations:")
335 logger.write("%s\n%s" % (src.printcolors.printcWarning(msg),
339 # construct the name of the directory that will contain the binaries
340 binaries_dir_name = "BINARIES-" + config.VARS.dist
342 # construct the correlation table between the product names, there
343 # actual install directories and there install directory in archive
345 for prod_name, install_dir in l_install_dir:
346 path_in_archive = os.path.join(binaries_dir_name, prod_name)
347 d_products[prod_name] = (install_dir, path_in_archive)
349 # create the relative launcher and add it to the files to add
350 if "profile" in config.APPLICATION:
351 launcher_name = config.APPLICATION.profile.launcher_name
352 launcher_package = produce_relative_launcher(config,
357 not(options.without_commercial))
359 d_products["launcher"] = (launcher_package, launcher_name)
361 # No profile, it means that there has to be some environment files
362 env_file = produce_relative_env_files(config,
367 d_products["environment file"] = (env_file, "env_launch.sh")
369 # And provide a script for the creation of an application EDF style
370 appli_script = product_appli_creation_script(config,
375 d_products["appli script"] = (env_file, appli_script)
379 def source_package(sat, config, logger, options, tmp_working_dir):
380 '''Prepare a dictionary that stores all the needed directories and files to
381 add in a source package.
383 :param config Config: The global configuration.
384 :param logger Logger: the logging instance
385 :param options OptResult: the options of the launched command
386 :param tmp_working_dir str: The temporary local directory containing some
387 specific directories or files needed in the
389 :return: the dictionary that stores all the needed directories and files to
390 add in a source package.
391 {label : (path_on_local_machine, path_in_archive)}
395 # Get all the products that are prepared using an archive
396 logger.write("Find archive products ... ")
397 d_archives, l_pinfo_vcs = get_archives(config, logger)
398 logger.write("Done\n")
400 if not options.with_vcs and len(l_pinfo_vcs) > 0:
401 # Make archives with the products that are not prepared using an archive
402 # (git, cvs, svn, etc)
403 logger.write("Construct archives for vcs products ... ")
404 d_archives_vcs = get_archives_vcs(l_pinfo_vcs,
409 logger.write("Done\n")
412 logger.write("Create the project ... ")
413 d_project = create_project_for_src_package(config,
416 logger.write("Done\n")
419 tmp_sat = add_salomeTools(config, tmp_working_dir)
420 d_sat = {"salomeTools" : (tmp_sat, "salomeTools")}
422 # Add a sat symbolic link if not win
423 if not src.architecture.is_windows():
424 tmp_satlink_path = os.path.join(tmp_working_dir, 'sat')
428 # In the jobs, os.getcwd() can fail
429 t = config.USER.workdir
430 os.chdir(tmp_working_dir)
431 if os.path.lexists(tmp_satlink_path):
432 os.remove(tmp_satlink_path)
433 os.symlink(os.path.join('salomeTools', 'sat'), 'sat')
436 d_sat["sat link"] = (tmp_satlink_path, "sat")
438 return src.merge_dicts(d_archives, d_archives_vcs, d_project, d_sat)
440 def get_archives(config, logger):
441 '''Find all the products that are get using an archive and all the products
442 that are get using a vcs (git, cvs, svn) repository.
444 :param config Config: The global configuration.
445 :param logger Logger: the logging instance
446 :return: the dictionary {name_product :
447 (local path of its archive, path in the package of its archive )}
448 and the list of specific configuration corresponding to the vcs
452 # Get the list of product informations
453 l_products_name = config.APPLICATION.products.keys()
454 l_product_info = src.product.get_products_infos(l_products_name,
458 for p_name, p_info in l_product_info:
459 # ignore the native and fixed products
460 if (src.product.product_is_native(p_info)
461 or src.product.product_is_fixed(p_info)):
463 if p_info.get_source == "archive":
464 archive_path = p_info.archive_info.archive_name
465 archive_name = os.path.basename(archive_path)
467 l_pinfo_vcs.append((p_name, p_info))
469 d_archives[p_name] = (archive_path,
470 os.path.join(ARCHIVE_DIR, archive_name))
471 return d_archives, l_pinfo_vcs
473 def add_salomeTools(config, tmp_working_dir):
474 '''Prepare a version of salomeTools that has a specific site.pyconf file
475 configured for a source package.
477 :param config Config: The global configuration.
478 :param tmp_working_dir str: The temporary local directory containing some
479 specific directories or files needed in the
481 :return: The path to the local salomeTools directory to add in the package
484 # Copy sat in the temporary working directory
485 sat_tmp_path = src.Path(os.path.join(tmp_working_dir, "salomeTools"))
486 sat_running_path = src.Path(config.VARS.salometoolsway)
487 sat_running_path.copy(sat_tmp_path)
489 # Update the site.pyconf file that contains the path to the project
490 site_pyconf_name = "site.pyconf"
491 site_pyconf_dir = os.path.join(tmp_working_dir, "salomeTools", "data")
492 site_pyconf_file = os.path.join(site_pyconf_dir, site_pyconf_name)
493 ff = open(site_pyconf_file, "w")
494 ff.write(SITE_TEMPLATE)
497 return sat_tmp_path.path
499 def get_archives_vcs(l_pinfo_vcs, sat, config, logger, tmp_working_dir):
500 '''For sources package that require that all products are get using an
501 archive, one has to create some archive for the vcs products.
502 So this method calls the clean and source command of sat and then create
505 :param l_pinfo_vcs List: The list of specific configuration corresponding to
507 :param sat Sat: The Sat instance that can be called to clean and source the
509 :param config Config: The global configuration.
510 :param logger Logger: the logging instance
511 :param tmp_working_dir str: The temporary local directory containing some
512 specific directories or files needed in the
514 :return: the dictionary that stores all the archives to add in the source
515 package. {label : (path_on_local_machine, path_in_archive)}
518 # clean the source directory of all the vcs products, then use the source
519 # command and thus construct an archive that will not contain the patches
520 l_prod_names = [pn for pn, __ in l_pinfo_vcs]
522 logger.write(_("clean sources\n"))
523 args_clean = config.VARS.application
524 args_clean += " --sources --products "
525 args_clean += ",".join(l_prod_names)
526 sat.clean(args_clean, batch=True, verbose=0, logger_add_link = logger)
528 logger.write(_("get sources"))
529 args_source = config.VARS.application
530 args_source += " --products "
531 args_source += ",".join(l_prod_names)
532 sat.source(args_source, batch=True, verbose=0, logger_add_link = logger)
534 # make the new archives
536 for pn, pinfo in l_pinfo_vcs:
537 path_archive = make_archive(pn, pinfo, tmp_working_dir)
538 d_archives_vcs[pn] = (path_archive,
539 os.path.join(ARCHIVE_DIR, pn + ".tgz"))
540 return d_archives_vcs
542 def make_archive(prod_name, prod_info, where):
543 '''Create an archive of a product by searching its source directory.
545 :param prod_name str: The name of the product.
546 :param prod_info Config: The specific configuration corresponding to the
548 :param where str: The path of the repository where to put the resulting
550 :return: The path of the resulting archive
553 path_targz_prod = os.path.join(where, prod_name + ".tgz")
554 tar_prod = tarfile.open(path_targz_prod, mode='w:gz')
555 local_path = prod_info.source_dir
556 tar_prod.add(local_path, arcname=prod_name)
558 return path_targz_prod
560 def create_project_for_src_package(config, tmp_working_dir, with_vcs):
561 '''Create a specific project for a source package.
563 :param config Config: The global configuration.
564 :param tmp_working_dir str: The temporary local directory containing some
565 specific directories or files needed in the
567 :param with_vcs boolean: True if the package is with vcs products (not
568 transformed into archive products)
569 :return: The dictionary
570 {"project" : (produced project, project path in the archive)}
574 # Create in the working temporary directory the full project tree
575 project_tmp_dir = os.path.join(tmp_working_dir, PROJECT_DIR)
576 products_pyconf_tmp_dir = os.path.join(project_tmp_dir,
578 compil_scripts_tmp_dir = os.path.join(project_tmp_dir,
581 env_scripts_tmp_dir = os.path.join(project_tmp_dir,
584 patches_tmp_dir = os.path.join(project_tmp_dir,
587 application_tmp_dir = os.path.join(project_tmp_dir,
589 for directory in [project_tmp_dir,
590 compil_scripts_tmp_dir,
593 application_tmp_dir]:
594 src.ensure_path_exists(directory)
596 # Create the pyconf that contains the information of the project
597 project_pyconf_name = "project.pyconf"
598 project_pyconf_file = os.path.join(project_tmp_dir, project_pyconf_name)
599 ff = open(project_pyconf_file, "w")
600 ff.write(PROJECT_TEMPLATE)
603 # Loop over the products to get there pyconf and all the scripts
604 # (compilation, environment, patches)
605 # and create the pyconf file to add to the project
606 lproducts_name = config.APPLICATION.products.keys()
607 l_products = src.product.get_products_infos(lproducts_name, config)
608 for p_name, p_info in l_products:
609 # ignore native and fixed products
610 if (src.product.product_is_native(p_info) or
611 src.product.product_is_fixed(p_info)):
613 find_product_scripts_and_pyconf(p_name,
617 compil_scripts_tmp_dir,
620 products_pyconf_tmp_dir)
622 find_application_pyconf(config, application_tmp_dir)
624 d_project = {"project" : (project_tmp_dir, PROJECT_DIR )}
627 def find_product_scripts_and_pyconf(p_name,
631 compil_scripts_tmp_dir,
634 products_pyconf_tmp_dir):
635 '''Create a specific pyconf file for a given product. Get its environment
636 script, its compilation script and patches and put it in the temporary
637 working directory. This method is used in the source package in order to
638 construct the specific project.
640 :param p_name str: The name of the product.
641 :param p_info Config: The specific configuration corresponding to the
643 :param config Config: The global configuration.
644 :param with_vcs boolean: True if the package is with vcs products (not
645 transformed into archive products)
646 :param compil_scripts_tmp_dir str: The path to the temporary compilation
647 scripts directory of the project.
648 :param env_scripts_tmp_dir str: The path to the temporary environment script
649 directory of the project.
650 :param patches_tmp_dir str: The path to the temporary patch scripts
651 directory of the project.
652 :param products_pyconf_tmp_dir str: The path to the temporary product
653 scripts directory of the project.
656 # read the pyconf of the product
657 product_pyconf_path = src.find_file_in_lpath(p_name + ".pyconf",
658 config.PATHS.PRODUCTPATH)
659 product_pyconf_cfg = src.pyconf.Config(product_pyconf_path)
661 # find the compilation script if any
662 if src.product.product_has_script(p_info):
663 compil_script_path = src.Path(p_info.compil_script)
664 compil_script_path.copy(compil_scripts_tmp_dir)
665 product_pyconf_cfg[p_info.section].compil_script = os.path.basename(
666 p_info.compil_script)
667 # find the environment script if any
668 if src.product.product_has_env_script(p_info):
669 env_script_path = src.Path(p_info.environ.env_script)
670 env_script_path.copy(env_scripts_tmp_dir)
671 product_pyconf_cfg[p_info.section].environ.env_script = os.path.basename(
672 p_info.environ.env_script)
673 # find the patches if any
674 if src.product.product_has_patches(p_info):
675 patches = src.pyconf.Sequence()
676 for patch_path in p_info.patches:
677 p_path = src.Path(patch_path)
678 p_path.copy(patches_tmp_dir)
679 patches.append(os.path.basename(patch_path), "")
681 product_pyconf_cfg[p_info.section].patches = patches
684 # put in the pyconf file the resolved values
685 for info in ["git_info", "cvs_info", "svn_info"]:
687 for key in p_info[info]:
688 product_pyconf_cfg[p_info.section][info][key] = p_info[
691 # if the product is not archive, then make it become archive.
692 if src.product.product_is_vcs(p_info):
693 product_pyconf_cfg[p_info.section].get_source = "archive"
694 if not "archive_info" in product_pyconf_cfg[p_info.section]:
695 product_pyconf_cfg[p_info.section].addMapping("archive_info",
696 src.pyconf.Mapping(product_pyconf_cfg),
698 product_pyconf_cfg[p_info.section
699 ].archive_info.archive_name = p_info.name + ".tgz"
701 # write the pyconf file to the temporary project location
702 product_tmp_pyconf_path = os.path.join(products_pyconf_tmp_dir,
704 ff = open(product_tmp_pyconf_path, 'w')
705 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
706 product_pyconf_cfg.__save__(ff, 1)
709 def find_application_pyconf(config, application_tmp_dir):
710 '''Find the application pyconf file and put it in the specific temporary
711 directory containing the specific project of a source package.
713 :param config Config: The global configuration.
714 :param application_tmp_dir str: The path to the temporary application
715 scripts directory of the project.
717 # read the pyconf of the application
718 application_name = config.VARS.application
719 application_pyconf_path = src.find_file_in_lpath(
720 application_name + ".pyconf",
721 config.PATHS.APPLICATIONPATH)
722 application_pyconf_cfg = src.pyconf.Config(application_pyconf_path)
725 application_pyconf_cfg.APPLICATION.workdir = src.pyconf.Reference(
726 application_pyconf_cfg,
728 'VARS.salometoolsway + $VARS.sep + ".."')
730 # Prevent from compilation in base
731 application_pyconf_cfg.APPLICATION.no_base = "yes"
733 # write the pyconf file to the temporary application location
734 application_tmp_pyconf_path = os.path.join(application_tmp_dir,
735 application_name + ".pyconf")
736 ff = open(application_tmp_pyconf_path, 'w')
737 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
738 application_pyconf_cfg.__save__(ff, 1)
741 def project_package(project_file_path, tmp_working_dir):
742 '''Prepare a dictionary that stores all the needed directories and files to
743 add in a project package.
745 :param project_file_path str: The path to the local project.
746 :param tmp_working_dir str: The temporary local directory containing some
747 specific directories or files needed in the
749 :return: the dictionary that stores all the needed directories and files to
750 add in a project package.
751 {label : (path_on_local_machine, path_in_archive)}
755 # Read the project file and get the directories to add to the package
756 project_pyconf_cfg = src.pyconf.Config(project_file_path)
757 paths = {"ARCHIVEPATH" : "archives",
758 "APPLICATIONPATH" : "applications",
759 "PRODUCTPATH" : "products",
761 "MACHINEPATH" : "machines"}
762 # Loop over the project paths and add it
764 if path not in project_pyconf_cfg:
766 # Add the directory to the files to add in the package
767 d_project[path] = (project_pyconf_cfg[path], paths[path])
768 # Modify the value of the path in the package
769 project_pyconf_cfg[path] = src.pyconf.Reference(
772 'project_path + "/' + paths[path] + '"')
775 if "project_path" not in project_pyconf_cfg:
776 project_pyconf_cfg.addMapping("project_path",
777 src.pyconf.Mapping(project_pyconf_cfg),
779 project_pyconf_cfg.project_path = src.pyconf.Reference(project_pyconf_cfg,
783 # Write the project pyconf file
784 project_file_name = os.path.basename(project_file_path)
785 project_pyconf_tmp_path = os.path.join(tmp_working_dir, project_file_name)
786 ff = open(project_pyconf_tmp_path, 'w')
787 ff.write("#!/usr/bin/env python\n#-*- coding:utf-8 -*-\n\n")
788 project_pyconf_cfg.__save__(ff, 1)
790 d_project["Project hat file"] = (project_pyconf_tmp_path, project_file_name)
794 def add_readme(config, package_type, where):
795 readme_path = os.path.join(where, "README")
796 f = open(readme_path, 'w')
797 # prepare substitution dictionary
799 if package_type == BINARY:
800 d['application'] = config.VARS.application
801 d['user'] = config.VARS.user
802 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
803 d['version'] = config.INTERNAL.sat_version
804 d['dist'] = config.VARS.dist
805 if 'profile' in config.APPLICATION:
806 d['launcher'] = config.APPLICATION.profile.launcher_name
807 readme_template_path = os.path.join(config.VARS.internal_dir,
808 "README_BIN.template")
810 d['env_file'] = 'env_launch.sh'
811 readme_template_path = os.path.join(config.VARS.internal_dir,
812 "README_BIN_NO_PROFILE.template")
814 if package_type == SOURCE:
815 d['application'] = config.VARS.application
816 d['user'] = config.VARS.user
817 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
818 d['version'] = config.INTERNAL.sat_version
819 if 'profile' in config.APPLICATION:
820 d['profile'] = config.APPLICATION.profile.product
821 d['launcher'] = config.APPLICATION.profile.launcher_name
822 readme_template_path = os.path.join(config.VARS.internal_dir,
823 "README_SRC.template")
825 if package_type == PROJECT:
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 readme_template_path = os.path.join(config.VARS.internal_dir,
830 "README_PROJECT.template")
832 if package_type == SAT:
833 d['user'] = config.VARS.user
834 d['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
835 d['version'] = config.INTERNAL.sat_version
836 readme_template_path = os.path.join(config.VARS.internal_dir,
837 "README_SAT.template")
839 f.write(src.template.substitute(readme_template_path, d))
845 '''method that is called when salomeTools is called with --help option.
847 :return: The text to display for the package command description.
850 return _("The package command creates an archive.\nThere are 4 kinds of "
851 "archive:\n 1- The binary archive. It contains all the product "
852 "installation directories and a launcher,\n 2- The sources archive."
853 " It contains the products archives, a project corresponding to "
854 "the application and salomeTools,\n 3- The project archive. It "
855 "contains a project (give the project file path as argument),\n 4-"
856 " The salomeTools archive. It contains salomeTools.\n\nexample:"
857 "\nsat package SALOME-master --sources")
859 def run(args, runner, logger):
860 '''method that is called when salomeTools is called with package parameter.
864 (options, args) = parser.parse_args(args)
866 # Check that a type of package is called, and only one
867 all_option_types = (options.binaries,
869 options.project not in ["", None],
872 # Check if no option for package type
873 if all_option_types.count(True) == 0:
874 msg = _("Error: Precise a type for the package\nUse one of the "
875 "following options: --binaries, --sources, --project or"
877 logger.write(src.printcolors.printcError(msg), 1)
878 logger.write("\n", 1)
881 # Check for only one option for package type
882 if all_option_types.count(True) > 1:
883 msg = _("Error: You can use only one type for the package\nUse only one"
884 " of the following options: --binaries, --sources, --project or"
886 logger.write(src.printcolors.printcError(msg), 1)
887 logger.write("\n", 1)
890 # Get the package type
892 package_type = BINARY
894 package_type = SOURCE
896 package_type = PROJECT
900 # The repository where to put the package if not Binary or Source
901 package_default_path = runner.cfg.USER.workdir
903 if package_type in [BINARY, SOURCE]:
904 # Check that the command has been called with an application
905 src.check_config_has_application(runner.cfg)
907 # Display information
908 logger.write(_("Packaging application %s\n") % src.printcolors.printcLabel(
909 runner.cfg.VARS.application), 1)
911 # Get the default directory where to put the packages
912 package_default_path = os.path.join(runner.cfg.APPLICATION.workdir,
914 src.ensure_path_exists(package_default_path)
916 elif package_type == PROJECT:
917 # check that the project is visible by SAT
918 if options.project not in runner.cfg.PROJECTS.project_file_paths:
919 site_path = os.path.join(runner.cfg.VARS.salometoolsway,
922 msg = _("ERROR: the project %(proj)s is not visible by salomeTools."
923 "\nPlease add it in the %(site)s file." % {
924 "proj" : options.project, "site" : site_path})
925 logger.write(src.printcolors.printcError(msg), 1)
926 logger.write("\n", 1)
930 src.printcolors.print_value(logger, "Package type", package_type, 2)
932 # get the name of the archive or construct it
934 if os.path.basename(options.name) == options.name:
935 # only a name (not a path)
936 archive_name = options.name
937 dir_name = package_default_path
939 archive_name = os.path.basename(options.name)
940 dir_name = os.path.dirname(options.name)
943 if archive_name[-len(".tgz"):] == ".tgz":
944 archive_name = archive_name[:-len(".tgz")]
945 if archive_name[-len(".tar.gz"):] == ".tar.gz":
946 archive_name = archive_name[:-len(".tar.gz")]
949 dir_name = package_default_path
950 if package_type == BINARY:
951 archive_name = (runner.cfg.APPLICATION.name +
953 runner.cfg.VARS.dist)
955 if package_type == SOURCE:
956 archive_name = (runner.cfg.APPLICATION.name +
960 archive_name = (runner.cfg.APPLICATION.name +
966 if package_type == PROJECT:
967 project_name, __ = os.path.splitext(
968 os.path.basename(options.project))
969 archive_name = ("PROJECT" +
973 if package_type == SAT:
974 archive_name = ("salomeTools" +
976 runner.cfg.INTERNAL.sat_version)
978 path_targz = os.path.join(dir_name, archive_name + ".tgz")
980 # Print the path of the package
981 src.printcolors.print_value(logger, "Package path", path_targz, 2)
983 # Create a working directory for all files that are produced during the
984 # package creation and that will be removed at the end of the command
985 tmp_working_dir = os.path.join(runner.cfg.VARS.tmp_root,
986 runner.cfg.VARS.datehour)
987 src.ensure_path_exists(tmp_working_dir)
988 logger.write("\n", 5)
989 logger.write(_("The temporary working directory: %s\n" % tmp_working_dir),5)
991 logger.write("\n", 3)
993 msg = _("Preparation of files to add to the archive")
994 logger.write(src.printcolors.printcLabel(msg), 2)
995 logger.write("\n", 2)
997 if package_type == BINARY:
998 d_files_to_add = binary_package(runner.cfg,
1002 if not(d_files_to_add):
1005 if package_type == SOURCE:
1006 d_files_to_add = source_package(runner,
1012 if package_type == PROJECT:
1013 d_files_to_add = project_package(options.project, tmp_working_dir)
1015 if package_type == SAT:
1016 d_files_to_add = {"salomeTools" : (runner.cfg.VARS.salometoolsway, "")}
1018 # Add the README file in the package
1019 local_readme_tmp_path = add_readme(runner.cfg,
1022 d_files_to_add["README"] = (local_readme_tmp_path, "README")
1024 # Add the additional files of option add_files
1025 if options.add_files:
1026 for file_path in options.add_files:
1027 if not os.path.exists(file_path):
1028 msg = _("WARNING: the file %s is not accessible.\n" % file_path)
1030 file_name = os.path.basename(file_path)
1031 d_files_to_add[file_name] = (file_path, file_name)
1033 logger.write("\n", 2)
1035 logger.write(src.printcolors.printcLabel(_("Actually do the package")), 2)
1036 logger.write("\n", 2)
1039 # Creating the object tarfile
1040 tar = tarfile.open(path_targz, mode='w:gz')
1042 # Add the files to the tarfile object
1043 res = add_files(tar, archive_name, d_files_to_add, logger)
1045 except KeyboardInterrupt:
1046 logger.write(src.printcolors.printcError("\nERROR: forced interruption\n"), 1)
1047 logger.write(_("Removing the temporary working directory ... "), 1)
1048 # remove the working directory
1049 shutil.rmtree(tmp_working_dir)
1050 logger.write(_("OK"), 1)
1051 logger.write(_("\n"), 1)
1054 # remove the working directory
1055 shutil.rmtree(tmp_working_dir)
1057 # Print again the path of the package
1058 logger.write("\n", 2)
1059 src.printcolors.print_value(logger, "Package path", path_targz, 2)