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
|
# Copyright 2016 Christoph Reiter
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# Deletes unneeded DLLs and checks DLL dependencies.
from typing import Optional
import logging
import os
import subprocess
import sys
import gi
gi.require_version('GIRepository', '2.0')
from gi.repository import GIRepository # noqa: E402
logging.basicConfig(level='INFO', format='%(levelname)s: %(message)s')
log = logging.getLogger()
def get_required_by_typelibs() -> set[str]:
deps: set[str] = set()
repo = GIRepository.Repository()
for tl in os.listdir(repo.get_search_path()[0]):
namespace, version = os.path.splitext(tl)[0].split('-', 1)
repo.require(namespace, version, 0)
lib = repo.get_shared_library(namespace)
if lib:
deps.update(lib.split(','))
return deps
EXTENSIONS = ['.exe', '.pyd', '.dll']
SYSTEM_LIBS = [
'advapi32.dll',
'cabinet.dll', 'comctl32.dll', 'comdlg32.dll', 'crypt32.dll', 'd3d9.dll',
'dnsapi.dll', 'dsound.dll', 'dwmapi.dll', 'gdi32.dll', 'imm32.dll',
'iphlpapi.dll', 'kernel32.dll', 'ksuser.dll', 'msi.dll', 'msimg32.dll',
'msvcr71.dll', 'msvcr80.dll', 'msvcrt.dll', 'ole32.dll', 'oleaut32.dll',
'opengl32.dll', 'rpcrt4.dll', 'setupapi.dll', 'shell32.dll', 'user32.dll',
'usp10.dll', 'winmm.dll', 'winspool.drv', 'wldap32.dll', 'ws2_32.dll',
'wsock32.dll', 'shlwapi.dll'
]
def get_dependencies(filename: str) -> list[str]:
deps: list[str] = []
try:
data = subprocess.getoutput('objdump -p %s' % filename)
except Exception as error:
log.error(error)
return deps
for line in data.splitlines():
line = line.strip()
if line.startswith('DLL Name:'):
deps.append(line.split(':', 1)[-1].strip().lower())
return deps
def find_lib(root: str, name: str) -> Optional[str]:
search_path = os.path.join(root, 'bin')
if os.path.exists(os.path.join(search_path, name)):
return os.path.join(search_path, name)
elif name in SYSTEM_LIBS:
return name
def get_things_to_delete(root: str) -> list[str]:
all_libs: set[str] = set()
needed: set[str] = set()
for base, _, files in os.walk(root):
for f in files:
path = os.path.join(base, f)
if os.path.splitext(path)[-1].lower() in EXTENSIONS:
all_libs.add(f.lower())
for lib in get_dependencies(path):
all_libs.add(lib)
needed.add(lib)
if find_lib(root, lib) is None:
log.info('MISSING: %s %s', path, lib)
for lib in get_required_by_typelibs():
needed.add(lib)
if find_lib(root, lib) is None:
log.info('MISSING: %s', lib)
result: list[str] = []
libs = all_libs - needed
for lib in libs:
_, ext = os.path.splitext(lib)
if ext == '.exe':
continue
name = find_lib(root, lib)
if name is None:
continue
result.append(name)
return result
def main() -> None:
libs = get_things_to_delete(sys.prefix)
while libs:
for lib in libs:
log.info('DELETE: %s', lib)
os.unlink(lib)
libs = get_things_to_delete(sys.prefix)
if __name__ == '__main__':
main()
|