aboutsummaryrefslogtreecommitdiff
blob: 918d4dcf4a3f53776cc294e5085d4b4e0b254332 (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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/python

# Copyright 2003-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2


import os
import shutil
import sys

import gentoolkit.pprinter as pp
import portage
from portage.emaint.main import TaskHandler
from portage.emaint.modules.binhost import binhost


class CleanUp:
    """Performs all cleaning actions to distfiles or package directories.

    @param controller: a progress output/user interaction controller function
                                       which returns a Boolean to control file deletion
                                       or bypassing/ignoring
    """

    def __init__(self, controller, quiet):
        self.controller = controller
        self.quiet = quiet

    def clean_dist(self, clean_dict, vcs):
        """Calculate size of each entry for display, prompt user if needed,
        delete files if approved and return the total size of files that
        have been deleted.

        @param clean_dict: dictionary of {'display name':[list of files]}

        @rtype: int
        @return: total size that was cleaned
        """
        file_type = "file"
        clean_size = 0
        # clean all entries one by one; sorting helps reading
        for key in sorted(clean_dict):
            clean_size += self._clean_files(clean_dict[key], key, file_type)
        # return total size of deleted or to delete files
        clean_size += self._clean_vcs_src(vcs)
        return clean_size

    def clean_pkgs(self, clean_dict, pkgdir):
        """Calculate size of each entry for display, prompt user if needed,
        delete files if approved and return the total size of files that
        have been deleted.

        @param clean_dict:  dictionary of  {'display name':[list of files]}
        @param metadata: package index of type portage.getbinpkg.PackageIndex()
        @param pkgdir: path to the package directory to be cleaned

        @rtype: int
        @return: total size that was cleaned
        """
        file_type = "binary package"
        clean_size = 0
        # clean all entries one by one; sorting helps reading
        for key in sorted(clean_dict):
            clean_size += self._clean_files(clean_dict[key], key, file_type)

        #  run 'emaint --fix' here
        if clean_size:
            file = os.path.join(portage.settings["PKGDIR"], "Packages")
            size1 = os.stat(file).st_size
            TaskHandler(show_progress_bar=self.quiet).run_tasks(
                [binhost.BinhostHandler], "fix"
            )
            size = size1 - os.stat(file).st_size
            self.controller(size, "Packages Index", file, "Index")
            clean_size += size
        # return total size of deleted or to delete files
        return clean_size

    def pretend_clean(self, clean_dict, vcs={}):
        """Shortcut function that calculates total space savings
        for the files in clean_dict.

        @param clean_dict: dictionary of {'display name':[list of files]}
        @rtype: integer
        @return: total size that would be cleaned
        """
        file_type = "file"
        clean_size = 0
        # tally all entries one by one; sorting helps reading
        if vcs:
            clean_size += self._clean_vcs_src(vcs, pretend=True)
        for key in sorted(clean_dict):
            key_size = self._get_size(clean_dict[key])
            self.controller(key_size, key, clean_dict[key], file_type)
            clean_size += key_size
        return clean_size

    def _get_size(self, key):
        """Determine the total size for an entry (may be several files)."""
        key_size = 0
        for file_ in key:
            # print file_
            # get total size for an entry (may be several files, and
            # links don't count
            # ...get its statinfo
            try:
                statinfo = os.stat(file_)
                if statinfo.st_nlink == 1:
                    key_size += statinfo.st_size
            except OSError as er:
                print(pp.error("Could not get stat info for:" + file_), file=sys.stderr)
                print(pp.error("Error: %s" % str(er)), file=sys.stderr)
        return key_size

    def _clean_files(self, files, key, file_type):
        """File removal function."""
        clean_size = 0
        for file_ in files:
            # print file_, type(file_)
            # ...get its statinfo
            try:
                statinfo = os.stat(file_)
            except OSError as er:
                if not os.path.exists(os.readlink(file_)):
                    try:
                        os.remove(file_)
                        print(
                            pp.error("Removed broken symbolic link " + file_),
                            file=sys.stderr,
                        )
                        break
                    except OSError as er:
                        print(
                            pp.error("Error deleting broken symbolic link " + file_),
                            file=sys.stderr,
                        )
                        print(pp.error("Error: %s" % str(er)), file=sys.stderr)
                        break
                else:
                    print(
                        pp.error("Could not get stat info for:" + file_),
                        file=sys.stderr,
                    )
                    print(pp.error("Error: %s" % str(er)), file=sys.stderr)
            if self.controller(statinfo.st_size, key, file_, file_type):
                # ... try to delete it.
                try:
                    os.unlink(file_)
                    # only count size if successfully deleted and not a link
                    if statinfo.st_nlink == 1:
                        clean_size += statinfo.st_size
                        try:
                            os.rmdir(os.path.dirname(file_))
                        except OSError:
                            pass
                except OSError as er:
                    print(pp.error("Could not delete " + file_), file=sys.stderr)
                    print(pp.error("Error: %s" % str(er)), file=sys.stderr)
        return clean_size

    def _clean_vcs_src(self, deprecated_vcs, pretend=False):
        clean_size = 0
        for checkout in deprecated_vcs:
            csize = 0
            for path, dirs, files in os.walk(checkout):
                for f in files:
                    fp = os.path.join(path, f)
                    try:
                        statinfo = os.stat(fp)
                    except OSError as er:
                        print(
                            pp.error("Could not get stat info for:" + fp),
                            file=sys.stderr,
                        )
                        print(pp.error("Error: %s" % str(er)), file=sys.stderr)
                    clean_size += statinfo.st_size
                    csize += statinfo.st_size

            try:
                self.controller(csize, checkout, checkout, "checkout")
                if not pretend:
                    shutil.rmtree(checkout)
            except OSError as er:
                print(pp.error("Could not delete " + checkout), file=sys.stderr)
                print(pp.error("Error: %s" % str(er)), file=sys.stderr)
        return clean_size