Beispiel #1
0
 def read_admin_config_file(self):
     config_path = var_stack.resolve_var("ADMIN_GUI_CONFIG_FILE", default="")
     if os.path.isfile(config_path):
         var_stack.get_configVar_obj("__SEARCH_PATHS__").clear_values() # so __include__ file will not be found on old paths
         self.read_yaml_file(config_path)
         self.admin_config_file_dirty = False
     else:
         print("File not found:", config_path)
Beispiel #2
0
 def read_yaml_file(self, file_path):
     logging.info("%s", file_path)
     with open_for_read_file_or_url(file_path, self.path_searcher) as file_fd:
         for a_node in yaml.compose_all(file_fd):
             if self.is_acceptable_yaml_doc(a_node):
                 if a_node.tag.startswith('!define_const'):
                     self.read_const_defines(a_node)
                 elif a_node.tag.startswith('!define'):
                     self.read_defines(a_node)
                 elif a_node.tag.startswith('!index'):
                     self.read_index(a_node)
                 else:
                     logging.error("Unknown document tag '%s' while reading file %s; Tag should be one of: !define, !index'", a_node.tag, file_path)
     var_list.get_configVar_obj("__READ_YAML_FILES__").append(file_path)
Beispiel #3
0
    def do_verify_index(self):
        self.read_yaml_file(var_list.resolve("$(__MAIN_INPUT_FILE__)"))
        info_map = svnTree.SVNTree()
        with open_for_read_file_or_url(var_list.resolve("$(INFO_MAP_FILE_URL)")) as rfd:
            info_map.read_from_text(rfd)

        # for each iid get full paths to it's sources
        iid_to_sources = defaultdict(list)
        InstallItem.begin_get_for_all_oses()
        for iid in sorted(self.install_definitions_index):
            with self.install_definitions_index[iid]:
                for source_var in var_list.get_configVar_obj("iid_source_var_list"):
                    source = var_list.resolve_var_to_list(source_var)
                    if source[2] in ("common", "Mac"):
                        iid_to_sources[iid].append( ("/".join( ("Mac", source[0])), source[1]))
                    if source[2] in ("common", "Win", "Win32", "Win64"):
                        iid_to_sources[iid].append( ("/".join( ("Win", source[0])), source[1]))

        for iid in sorted(iid_to_sources):
            with self.install_definitions_index[iid]:
                iid_problem_messages = list()
                # check inherits
                for inheritee in var_list.resolve_var_to_list("iid_inherite"):
                    if inheritee not in self.install_definitions_index:
                        iid_problem_messages.append(" ".join( ("inherits from non existing", inheritee ) ))
                # check depends
                for dependee in var_list.resolve_var_to_list("iid_depend_list"):
                    if dependee not in self.install_definitions_index:
                        iid_problem_messages.append(" ".join( ("depends on non existing", dependee ) ))
                # check sources
                for source in iid_to_sources[iid]:
                    map_item = info_map.get_item_at_path(source[0])
                    if map_item is None:
                        iid_problem_messages.append(" ".join( ("source", quoteme_single(source[0]), "does not exist") ))
                    else:
                        if source[1] in ("!dir", "!dir_cont", "!files"):
                            if map_item.isFile():
                                iid_problem_messages.append(" ".join( ("source", quoteme_single(source[0]), "is a file but type is", source[1]) ))
                            else:
                                file_list, dir_list = map_item.unsorted_sub_items()
                                if source[1] == "!files" and len(file_list) == 0:
                                    iid_problem_messages.append(" ".join( ("source", quoteme_single(source[0]), "has no files but type is", source[1]) ))
                                if source[1] in ("!dir", "!dir_cont") and len(file_list)+len(dir_list) == 0:
                                    iid_problem_messages.append(" ".join( ("source", quoteme_single(source[0]), "has no files or dirs but type is", source[1]) ))
                        if source[1] == "!file"  and not map_item.isFile():
                            iid_problem_messages.append(" ".join( ("source", quoteme_single(source[0]), "is a dir but type is", source[1]) ))
                if iid_problem_messages:
                    print(iid+":")
                    for problem_message in sorted(iid_problem_messages):
                        print("   ", problem_message)
        self.find_cycles()
        print("index:", len(self.install_definitions_index), "iids")
        num_files = info_map.num_subs_in_tree(what="file")
        num_dirs = info_map.num_subs_in_tree(what="dir")
        print("info map:", num_files, "files in", num_dirs, "folders")
    def create_download_instructions(self):
        self.instlObj.batch_accum.set_current_section('sync')
        self.instlObj.batch_accum += self.instlObj.platform_helper.progress("Starting sync from $(SYNC_BASE_URL)")
        self.sync_base_url = var_stack.resolve("$(SYNC_BASE_URL)")

        self.instlObj.batch_accum += self.instlObj.platform_helper.new_line()

        for iid in self.installState.full_install_items:
            with self.install_definitions_index[iid]:
                for source_var in var_stack.get_configVar_obj("iid_source_var_list"):
                    source = var_stack.resolve_var_to_list(source_var)
                    self.p4_sync_for_source(source)
Beispiel #5
0
 def __sort_install_items_by_target_folder(self, instlObj):
     for IID in self.full_install_items:
         with instlObj.install_definitions_index[IID] as installi:
             folder_list_for_idd = [folder for folder in var_list["iid_folder_list"]]
             if folder_list_for_idd:
                 for folder in folder_list_for_idd:
                     norm_folder = os.path.normpath(folder)
                     self.install_items_by_target_folder[norm_folder].append(IID)
             else: # items that need no copy
                 for source_var in var_list.get_configVar_obj("iid_source_var_list"):
                     source = var_list.resolve_var_to_list(source_var)
                     relative_sync_folder = instlObj.relative_sync_folder_for_source(source)
                     sync_folder =  os.path.join( "$(LOCAL_REPO_SOURCES_DIR)", relative_sync_folder )
                     self.no_copy_items_by_sync_folder[sync_folder].append(IID)
Beispiel #6
0
 def filter_out_unrequired_items(self):
     """ Removes from work_info_map items not required to be installed.
         First all items are marked False.
         Items required by each install source are then marked True.
         Finally items marked False and empty directories are removed.
     """
     self.work_info_map.set_user_data_all_recursive(False)
     for iid  in self.installState.full_install_items:
         with self.instlObj.install_definitions_index[iid] as installi:
             for source_var in var_list.get_configVar_obj("iid_source_var_list"):
                 source = var_list.resolve_var_to_list(source_var)
                 self.mark_required_items_for_source(source)
     self.work_info_map.recursive_remove_depth_first(is_user_data_false_or_dir_empty)
     self.work_info_map.write_to_file(var_list.resolve("$(REQUIRED_INFO_MAP_PATH)"), in_format="text")
Beispiel #7
0
    def __init__(self, initial_vars=None):
        # init objects owned by this class
        self.path_searcher = SearchPaths(var_list.get_configVar_obj("__SEARCH_PATHS__"))
        self.init_default_vars(initial_vars)
        # initialize the search paths helper with the current directory and dir where instl is now
        self.path_searcher.add_search_path(os.getcwd())
        self.path_searcher.add_search_path(os.path.dirname(os.path.realpath(sys.argv[0])))
        self.path_searcher.add_search_path(var_list.resolve("$(__INSTL_DATA_FOLDER__)"))

        self.platform_helper = PlatformSpecificHelperFactory(var_list.resolve("$(__CURRENT_OS__)"), self)
        self.platform_helper.init_copy_tool() # init initial copy tool, tool might be later overridden after reading variable COPY_TOOL from yaml.


        self.install_definitions_index = dict()
        self.batch_accum = BatchAccumulator()
        self.do_not_write_vars = ("INFO_MAP_SIG", "INDEX_SIG","PUBLIC_KEY")
        self.out_file_realpath = None
Beispiel #8
0
 def create_sync_instructions(self, installState):
     self.ii.batch_accum.set_current_section('sync')
     self.ii.batch_accum += self.ii.platform_helper.progress("Starting sync from $(SYNC_BASE_URL)/$(SOURCE_PREFIX)")
     self.ii.batch_accum += self.ii.platform_helper.mkdir("$(LOCAL_SYNC_DIR)")
     self.ii.batch_accum += self.ii.platform_helper.cd("$(LOCAL_SYNC_DIR)")
     self.ii.batch_accum.indent_level += 1
     self.ii.batch_accum += " ".join(('"$(SVN_CLIENT_PATH)"', "co", '"$(BOOKKEEPING_DIR_URL)"', '"$(REL_BOOKKIPING_PATH)"', "--revision", "$(REPO_REV)", "--depth", "infinity"))
     self.ii.batch_accum += self.ii.platform_helper.progress("instl folder file $(BOOKKEEPING_DIR_URL)?p=$(REPO_REV)")
     for iid  in installState.full_install_items:
         with self.install_definitions_index[iid]:
             for source_var in var_list.get_configVar_obj("iid_source_var_list"):
                 source = var_list.resolve_var_to_list(source_var)
                 self.ii.batch_accum += self.create_svn_sync_instructions_for_source(source)
             self.ii.batch_accum += self.ii.platform_helper.progress("Sync {}".format(var_list.resolve("iid_name")))
     for iid in installState.orphan_install_items:
         self.ii.batch_accum += self.ii.platform_helper.echo("Don't know how to sync "+iid)
     self.ii.batch_accum.indent_level -= 1
     self.ii.batch_accum += self.ii.platform_helper.echo("from $(SYNC_BASE_URL)/$(SOURCE_PREFIX)")
 def do_log(self, params):
     import pyinstl.log_utils
     top_logger = logging.getLogger()
     if params:
         params = shlex.split(params)
         if params[0].lower() == "debug":
             debug_log_file_path = pyinstl.log_utils.get_log_file_path(self.this_program_name, self.this_program_name, debug=True)
             if len(params) == 1 or params[1].lower() in ("on", "true", "yes"):
                 if top_logger.getEffectiveLevel() > pyinstl.log_utils.debug_logging_level or not os.path.isfile(debug_log_file_path):
                     pyinstl.log_utils.setup_file_logging(debug_log_file_path, pyinstl.log_utils.debug_logging_level)
                     pyinstl.log_utils.debug_logging_started = True
             elif params[1].lower() in ("off", "false", "no"):
                 top_logger.setLevel(pyinstl.log_utils.default_logging_level)
                 try:
                     pyinstl.log_utils.teardown_file_logging(debug_log_file_path, pyinstl.log_utils.default_logging_level)
                 except:
                     pass
             var_list.get_configVar_obj("LOG_FILE_DEBUG")[2] = pyinstl.log_utils.debug_logging_started
     self.report_logging_state()
Beispiel #10
0
    def defaults_help(self):
        defaults_folder_path = os.path.join(var_stack.resolve("$(__INSTL_DATA_FOLDER__)"), "defaults")
        for yaml_file in os.listdir(defaults_folder_path):
            if fnmatch.fnmatch(yaml_file, '*.yaml') and yaml_file != "P4.yaml": # hack to not read the P4 defaults
                self.instlObj.read_yaml_file(os.path.join(defaults_folder_path, yaml_file))
        defaults_list = [("Variable name", "Raw value", "Resolved value"),
                         ("_____________", "_________", "______________")]
        for var in sorted(var_stack):
            if not var.startswith("__"):
                cv = var_stack.get_configVar_obj(var)
                raw_value = " ".join([value for value in cv])
                resolved_value = var_stack.resolve_var(var)
                if raw_value != resolved_value:
                    defaults_list.append( (var, raw_value, resolved_value) )
                else:
                    defaults_list.append( (var, raw_value) )

        col_format = gen_col_format(max_widths(defaults_list))
        for res_line in defaults_list:
            print(col_format[len(res_line)].format(*res_line))
Beispiel #11
0
    def __init__(self, initial_vars=None):
        # init objects owned by this class

        # only when allow_reading_of_internal_vars is true, variables who's name begins and ends with "__"
        # can be read from file
        self.allow_reading_of_internal_vars = False
        self.path_searcher = SearchPaths(var_stack.get_configVar_obj("__SEARCH_PATHS__"))
        self.init_default_vars(initial_vars)
        # initialize the search paths helper with the current directory and dir where instl is now
        self.path_searcher.add_search_path(os.getcwd())
        self.path_searcher.add_search_path(os.path.dirname(os.path.realpath(sys.argv[0])))
        self.path_searcher.add_search_path(var_stack.resolve("$(__INSTL_DATA_FOLDER__)"))

        self.read_user_config()

        self.platform_helper = PlatformSpecificHelperFactory(var_stack.resolve("$(__CURRENT_OS__)"), self)
        # init initial copy tool, tool might be later overridden after reading variable COPY_TOOL from yaml.
        self.platform_helper.init_copy_tool()

        self.install_definitions_index = dict()
        self.batch_accum = BatchAccumulator()
        self.do_not_write_vars = ("INFO_MAP_SIG", "INDEX_SIG", "PUBLIC_KEY", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "__CREDENTIALS__")
        self.out_file_realpath = None
Beispiel #12
0
def create_remove_instructions(self):
    self.have_map = svnTree.SVNTree()

    have_info_path = var_stack.resolve("$(HAVE_INFO_MAP_PATH)")
    if not os.path.isfile(have_info_path):
        have_info_path = var_stack.resolve("$(SITE_HAVE_INFO_MAP_PATH)")
    self.have_map.read_info_map_from_file(have_info_path, a_format="text")

    self.batch_accum.set_current_section('remove')
    self.batch_accum += self.platform_helper.progress("Starting remove")
    sorted_target_folder_list = sorted(self.installState.install_items_by_target_folder,
                                       key=lambda fold: var_stack.resolve(fold),
                                       reverse=True)
    # print(sorted_target_folder_list)
    self.accumulate_unique_actions('pre_remove', self.installState.full_install_items)

    for folder_name in sorted_target_folder_list:
        var_stack.set_var("__TARGET_DIR__").append(os.path.normpath(folder_name))
        items_in_folder = self.installState.install_items_by_target_folder[folder_name]
        logging.info("folder %s", var_stack.resolve(folder_name))
        self.batch_accum += self.platform_helper.new_line()

        self.accumulate_unique_actions('pre_remove_from_folder', items_in_folder)

        for IID in items_in_folder:
            with self.install_definitions_index[IID] as installi:
                for source_var in var_stack.get_configVar_obj("iid_source_var_list"):
                    source = var_stack.resolve_var_to_list(source_var)
                    self.batch_accum += var_stack.resolve_var_to_list_if_exists("iid_action_list_pre_remove_item")
                    self.create_remove_instructions_for_source(folder_name, source)
                    self.batch_accum += var_stack.resolve_var_to_list_if_exists("iid_action_list_post_remove_item")
                    self.batch_accum += self.platform_helper.progress("Remove {installi.name}".format(**locals()))

        self.accumulate_unique_actions('post_remove_from_folder', items_in_folder)

    self.accumulate_unique_actions('post_remove', self.installState.full_install_items)
Beispiel #13
0
    def create_copy_instructions(self):
        # copy and actions instructions for sources
        self.batch_accum.set_current_section('copy')
        self.batch_accum += self.platform_helper.progress("Starting copy from $(LOCAL_REPO_SOURCES_DIR)")

        sorted_target_folder_list = sorted(self.installState.install_items_by_target_folder, key=lambda fold: var_list.resolve(fold))

        # first create all target folders so to avoid dependency order problems such as creating links between folders
        for folder_name in sorted_target_folder_list:
            self.batch_accum += self.platform_helper.mkdir_with_owner(folder_name)
        self.batch_accum += self.platform_helper.progress("Make directories done")

        self.accumulate_unique_actions('copy_in', self.installState.full_install_items)

        if 'Mac' in var_list.resolve_to_list("$(__CURRENT_OS_NAMES__)") and 'Mac' in var_list.resolve_to_list("$(TARGET_OS)"):
            self.batch_accum += self.platform_helper.resolve_symlink_files(in_dir="$(LOCAL_REPO_SOURCES_DIR)")
            self.batch_accum += self.platform_helper.progress("Resolve .symlink files")

            have_map = svnTree.SVNTree()
            have_info_path = var_list.resolve("$(NEW_HAVE_INFO_MAP_PATH)") # in case we're in synccopy command
            if not os.path.isfile(have_info_path):
                have_info_path = var_list.resolve("$(HAVE_INFO_MAP_PATH)") # in case we're in copy command
            if os.path.isfile(have_info_path):
                have_map.read_info_map_from_file(have_info_path, format="text")
                num_files_to_set_exec = have_map.num_subs_in_tree(what="file", predicate=lambda in_item: in_item.isExecutable())
                logging.info("Num files to set exec: %d", num_files_to_set_exec)
                if num_files_to_set_exec > 0:
                    self.batch_accum += self.platform_helper.pushd("$(LOCAL_REPO_SYNC_DIR)")
                    self.batch_accum += self.platform_helper.set_exec_for_folder(have_info_path)
                    self.platform_helper.num_items_for_progress_report += num_files_to_set_exec
                    self.batch_accum += self.platform_helper.progress("Set exec done")
                    self.batch_accum += self.platform_helper.new_line()
                    self.batch_accum += self.platform_helper.popd()

        for folder_name in sorted_target_folder_list:
            items_in_folder = self.installState.install_items_by_target_folder[folder_name]
            logging.info("folder %s", var_list.resolve(folder_name))
            self.batch_accum += self.platform_helper.new_line()
            self.batch_accum += self.platform_helper.cd(folder_name)

            # accumulate folder_in actions from all items, eliminating duplicates
            self.accumulate_unique_actions('folder_in', items_in_folder)

            batch_accum_len_before = len(self.batch_accum)
            self.batch_accum += self.platform_helper.copy_tool.begin_copy_folder()
            for IID in items_in_folder:
                with self.install_definitions_index[IID] as installi:
                    for source_var in var_list.get_configVar_obj("iid_source_var_list"):
                        source = var_list.resolve_var_to_list(source_var)
                        self.batch_accum += var_list.resolve_to_list("$(iid_action_list_before)")
                        self.create_copy_instructions_for_source(source)
                        self.batch_accum += var_list.resolve_to_list("$(iid_action_list_after)")
                        self.batch_accum += self.platform_helper.progress("Copy {installi.name}".format(**locals()))
            self.batch_accum += self.platform_helper.copy_tool.end_copy_folder()
            logging.info("... copy actions: %d", len(self.batch_accum) - batch_accum_len_before)

            # accumulate folder_out actions from all items, eliminating duplicates
            self.accumulate_unique_actions('folder_out', items_in_folder)

            self.batch_accum.indent_level -= 1

        # actions instructions for sources that do not need copying, here folder_name is the sync folder
        for folder_name, items_in_folder in self.installState.no_copy_items_by_sync_folder.iteritems():
            # calculate total number of actions for all items relating to folder_name, if 0 we can skip this folder altogether
            num_actions_for_folder = reduce(lambda x, y: x+len(self.install_definitions_index[y].all_action_list()), items_in_folder, 0)
            logging.info("%d non-copy items folder %s (%s)", num_actions_for_folder, folder_name, var_list.resolve(folder_name))

            if 0 == num_actions_for_folder:
                continue

            self.batch_accum += self.platform_helper.new_line()
            self.batch_accum += self.platform_helper.cd(folder_name)
            self.batch_accum.indent_level += 1

            # accumulate folder_in actions from all items, eliminating duplicates
            self.accumulate_unique_actions('folder_in', items_in_folder)

            for IID in items_in_folder:
                with self.install_definitions_index[IID]:
                    self.batch_accum += var_list.resolve_to_list("$(iid_action_list_before)")
                    self.batch_accum += var_list.resolve_to_list("$(iid_action_list_after)")

            # accumulate folder_out actions from all items, eliminating duplicates
            self.accumulate_unique_actions('folder_out', items_in_folder)

            self.batch_accum += self.platform_helper.progress("{folder_name}".format(**locals()))
            self.batch_accum.indent_level -= 1

        self.accumulate_unique_actions('copy_out', self.installState.full_install_items)

        self.platform_helper.copy_tool.finalize()

        # messages about orphan iids
        for iid in self.installState.orphan_install_items:
            logging.info("Orphan item: %s", iid)
            self.batch_accum += self.platform_helper.echo("Don't know how to install "+iid)
        self.batch_accum += self.platform_helper.progress("Done copy")
Beispiel #14
0
 def read_yaml_file(self, file_path):
     logging.info("%s", file_path)
     with open_for_read_file_or_url(file_path, self.path_searcher) as file_fd:
         buffer = StringIO.StringIO(file_fd.read())
         self.read_yaml_from_stream(buffer)
     var_stack.get_configVar_obj("__READ_YAML_FILES__").append(file_path)
Beispiel #15
0
 def read_yaml_from_stream(self, the_stream):
     for a_node in yaml.compose_all(the_stream):
         if self.is_acceptable_yaml_doc(a_node):
             if a_node.tag.startswith('!define_const'):
                 self.read_const_defines(a_node)
             elif a_node.tag.startswith('!define'):
                 self.read_defines(a_node)
             elif a_node.tag.startswith('!index'):
                 self.read_index(a_node)
             elif a_node.tag.startswith('!require'):
                 self.read_require(a_node)
             else:
                 logging.error(
                     "Unknown document tag '%s' while reading file %s; Tag should be one of: !define, !index'",
                     a_node.tag, file_path)
     if not self.check_version_compatibility():
         raise ValueError(var_stack.resolve("Minimal instl version $(INSTL_MINIMAL_VERSION) > current version $(__INSTL_VERSION__); ")+var_stack.get_configVar_obj("INSTL_MINIMAL_VERSION").description)
Beispiel #16
0
def create_copy_instructions(self):
    self.have_map = svnTree.SVNTree()
    # read NEW_HAVE_INFO_MAP_PATH and not HAVE_INFO_MAP_PATH. Copy might be called after the sync batch file was created
    # but before it was executed.  HAVE_INFO_MAP_PATH is only created
    # when the sync batch file is executed.
    have_info_path = var_stack.resolve("$(NEW_HAVE_INFO_MAP_PATH)")
    self.have_map.read_info_map_from_file(have_info_path, a_format="text")

    # copy and actions instructions for sources
    self.batch_accum.set_current_section('copy')
    self.batch_accum += self.platform_helper.progress("Starting copy from $(LOCAL_REPO_SYNC_DIR)")

    sorted_target_folder_list = sorted(self.installState.install_items_by_target_folder,
                                       key=lambda fold: var_stack.resolve(fold))

    # first create all target folders so to avoid dependency order problems such as creating links between folders
    if len(sorted_target_folder_list) > 0:
        self.batch_accum += self.platform_helper.progress("Creating folders...")
        for folder_name in sorted_target_folder_list:
            self.batch_accum += self.platform_helper.mkdir_with_owner(folder_name)
        self.batch_accum += self.platform_helper.progress("Create folders done")

    self.accumulate_unique_actions('pre_copy', self.installState.full_install_items)

    if 'Mac' in var_stack.resolve_to_list("$(__CURRENT_OS_NAMES__)") and 'Mac' in var_stack.resolve_to_list("$(TARGET_OS)"):
        self.pre_copy_mac_handling()

    for folder_name in sorted_target_folder_list:
        items_in_folder = self.installState.install_items_by_target_folder[folder_name]
        logging.info("folder %s", var_stack.resolve(folder_name))
        self.batch_accum += self.platform_helper.new_line()
        self.batch_accum += self.platform_helper.cd(folder_name)

        # accumulate pre_copy_to_folder actions from all items, eliminating duplicates
        self.accumulate_unique_actions('pre_copy_to_folder', items_in_folder)

        batch_accum_len_before = len(self.batch_accum)
        self.batch_accum += self.platform_helper.copy_tool.begin_copy_folder()
        for IID in items_in_folder:
            with self.install_definitions_index[IID] as installi:
                for source_var in var_stack.get_configVar_obj("iid_source_var_list"):
                    source = var_stack.resolve_var_to_list(source_var)
                    self.batch_accum += var_stack.resolve_var_to_list_if_exists("iid_action_list_pre_copy_item")
                    self.create_copy_instructions_for_source(source)
                    self.batch_accum += var_stack.resolve_var_to_list_if_exists("iid_action_list_post_copy_item")
                    self.batch_accum += self.platform_helper.progress("Copy {installi.name}".format(**locals()))
        self.batch_accum += self.platform_helper.copy_tool.end_copy_folder()
        logging.info("... copy actions: %d", len(self.batch_accum) - batch_accum_len_before)

        self.batch_accum += self.platform_helper.progress("Expanding files...")
        self.batch_accum += self.platform_helper.unwtar_current_folder(no_artifacts=True)
        self.batch_accum += self.platform_helper.progress("Expand files done")

        if 'Mac' in var_stack.resolve_to_list("$(__CURRENT_OS_NAMES__)") and 'Mac' in var_stack.resolve_to_list("$(TARGET_OS)"):
            self.batch_accum += self.platform_helper.progress("Resolving symlinks...")
            self.batch_accum += self.platform_helper.resolve_symlink_files()
            self.batch_accum += self.platform_helper.progress("Resolve symlinks done")

        # accumulate post_copy_to_folder actions from all items, eliminating duplicates
        self.accumulate_unique_actions('post_copy_to_folder', items_in_folder)

        self.batch_accum.indent_level -= 1

    # actions instructions for sources that do not need copying, here folder_name is the sync folder
    for folder_name, items_in_folder in self.installState.no_copy_items_by_sync_folder.iteritems():

        self.batch_accum += self.platform_helper.new_line()
        self.batch_accum += self.platform_helper.cd(folder_name)
        self.batch_accum.indent_level += 1

        # accumulate pre_copy_to_folder actions from all items, eliminating duplicates
        self.accumulate_unique_actions('pre_copy_to_folder', items_in_folder)


        for IID in items_in_folder:
            with self.install_definitions_index[IID]:
                for source_var in var_stack.resolve_var_to_list_if_exists("iid_source_var_list"):
                    source = var_stack.resolve_var_to_list(source_var)
                    source_folder, source_name = os.path.split(source[0])
                    to_untar = os.path.join(folder_name, source_name)
                    self.batch_accum += self.platform_helper.unwtar_something(to_untar)
                self.batch_accum += var_stack.resolve_var_to_list_if_exists("iid_action_list_pre_copy_item")
                self.batch_accum += var_stack.resolve_var_to_list_if_exists("iid_action_list_post_copy_item")

        # accumulate post_copy_to_folder actions from all items, eliminating duplicates
        self.accumulate_unique_actions('post_copy_to_folder', items_in_folder)

        self.batch_accum += self.platform_helper.progress("{folder_name}".format(**locals()))
        self.batch_accum.indent_level -= 1

    self.accumulate_unique_actions('post_copy', self.installState.full_install_items)

    self.batch_accum.set_current_section('post-copy')
    # Copy have_info file to "site" (e.g. /Library/Application support/... or c:\ProgramData\...)
    # for reference. But when preparing offline installers the site location is the same as the sync location
    # so copy should be avoided.
    if var_stack.resolve("$(HAVE_INFO_MAP_PATH)") != var_stack.resolve("$(SITE_HAVE_INFO_MAP_PATH)"):
        self.batch_accum += self.platform_helper.mkdir_with_owner("$(SITE_REPO_BOOKKEEPING_DIR)")
        self.batch_accum += self.platform_helper.copy_file_to_file("$(HAVE_INFO_MAP_PATH)", "$(SITE_HAVE_INFO_MAP_PATH)")

    self.platform_helper.copy_tool.finalize()

    self.create_require_file_instructions()

    # messages about orphan iids
    for iid in self.installState.orphan_install_items:
        logging.info("Orphan item: %s", iid)
        self.batch_accum += self.platform_helper.echo("Don't know how to install " + iid)
    self.batch_accum += self.platform_helper.progress("Done copy")