aboutsummaryrefslogtreecommitdiff
blob: 8024f24d7cdbe71f44544fb19498812ce03be535 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# Copyright 2020 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

from collections import OrderedDict
from collections.abc import Mapping
from hashlib import md5

from portage.localization import _
from portage.util import _recursive_file_list, writemsg
from portage.util.configparser import SafeConfigParser, ConfigParserError, read_configs


class BinRepoConfig:
    __slots__ = (
        "name",
        "name_fallback",
        "fetchcommand",
        "priority",
        "resumecommand",
        "sync_uri",
    )

    def __init__(self, opts):
        """
        Create a BinRepoConfig with options in opts.
        """
        for k in self.__slots__:
            setattr(self, k, opts.get(k.replace("_", "-")))

    def info_string(self):
        """
        Returns a formatted string containing informations about the repository.
        Used by emerge --info.
        """
        indent = " " * 4
        repo_msg = []
        repo_msg.append(self.name or self.name_fallback)
        if self.priority is not None:
            repo_msg.append(indent + "priority: " + str(self.priority))
        repo_msg.append(indent + "sync-uri: " + self.sync_uri)
        repo_msg.append("")
        return "\n".join(repo_msg)


class BinRepoConfigLoader(Mapping):
    def __init__(self, paths, settings):
        """Load config from files in paths"""

        # Defaults for value interpolation.
        parser_defaults = {
            "EPREFIX": settings["EPREFIX"],
            "EROOT": settings["EROOT"],
            "PORTAGE_CONFIGROOT": settings["PORTAGE_CONFIGROOT"],
            "ROOT": settings["ROOT"],
        }

        try:
            parser = self._parse(paths, parser_defaults)
        except ConfigParserError as e:
            writemsg(
                _("!!! Error while reading binrepo config file: %s\n") % e,
                noiselevel=-1,
            )
            parser = SafeConfigParser(defaults=parser_defaults)

        repos = []
        sync_uris = []
        for section_name in parser.sections():
            repo_data = dict(parser[section_name].items())
            repo_data["name"] = section_name
            repo = BinRepoConfig(repo_data)
            if repo.sync_uri is None:
                writemsg(
                    _("!!! Missing sync-uri setting for binrepo %s\n") % (repo.name,),
                    noiselevel=-1,
                )
                continue

            sync_uri = self._normalize_uri(repo.sync_uri)
            sync_uris.append(sync_uri)
            repo.sync_uri = sync_uri
            if repo.priority is not None:
                try:
                    repo.priority = int(repo.priority)
                except ValueError:
                    repo.priority = None
            repos.append(repo)

        sync_uris = set(sync_uris)
        current_priority = 0
        for sync_uri in reversed(settings.get("PORTAGE_BINHOST", "").split()):
            sync_uri = self._normalize_uri(sync_uri)
            if sync_uri not in sync_uris:
                current_priority += 1
                sync_uris.add(sync_uri)
                repos.append(
                    BinRepoConfig(
                        {
                            "name-fallback": self._digest_uri(sync_uri),
                            "name": None,
                            "priority": current_priority,
                            "sync-uri": sync_uri,
                        }
                    )
                )

        self._data = OrderedDict(
            (repo.name or repo.name_fallback, repo)
            for repo in sorted(
                repos,
                key=lambda repo: (repo.priority or 0, repo.name or repo.name_fallback),
            )
        )

    @staticmethod
    def _digest_uri(uri):
        return md5(uri.encode("utf_8")).hexdigest()

    @staticmethod
    def _normalize_uri(uri):
        return uri.rstrip("/")

    @staticmethod
    def _parse(paths, defaults):
        parser = SafeConfigParser(defaults=defaults)
        recursive_paths = []
        for p in paths:
            if isinstance(p, str):
                recursive_paths.extend(_recursive_file_list(p))
            else:
                recursive_paths.append(p)

        read_configs(parser, recursive_paths)
        return parser

    def __iter__(self):
        return iter(self._data)

    def __contains__(self, key):
        return key in self._data

    def __getitem__(self, key):
        return self._data[key]

    def __len__(self):
        return len(self._data)