Salome HOME
[bos #32522][EDF] SALOME on Demand. Small refact: changed log output, edit docstrings.
[modules/kernel.git] / bin / SalomeOnDemandTK / extension_query.py
1 #!/usr/bin/env python3
2 # -*- coding:utf-8 -*-
3 # Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
5 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
6 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
7 #
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 #
22 # See https://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #
24
25 #  File   : extension_query.py
26 #  Author : Konstantin Leontev, Open Cascade
27 #
28 #  @package SalomeOnDemandTK
29 #  @brief An utility package that will allow you to know the size of an extension
30 # and generate a dependency tree.
31
32 """
33 An utility package that will allow you to know the size of an extension
34 and generate a dependency tree.
35 """
36
37 import os
38 import sys
39 from traceback import format_exc
40
41 from .extension_utilities import logger, \
42     SALOME_EXTDIR, DFILE_EXT, EXTDEPENDSON_KEY, \
43     isvalid_dirname, find_salomexc, list_files_ext, read_salomexd
44
45
46 def size_to_str(size, format_in_bytes=False):
47     """
48     Returns a string describing a size.
49
50     Args:
51         size - the size to represent as a string.
52         format_in_bytes - if True, size is expressed in bytes,
53             otherwise human readable units are used.
54
55     Returns:
56         The size as a string with units.
57     """
58
59     if format_in_bytes:
60         return '%s' % size
61
62     __units__ = ["", "Ki", "Mi", "Gi", "Ti"]
63
64     kilo = 1024.0
65     prefix_index = 0
66     prefix_index_max = len(__units__) - 1
67     while (size > kilo and prefix_index < prefix_index_max):
68         prefix_index += 1
69         size /= kilo
70
71     unit = __units__[prefix_index]
72     return '%.2f %sB' % (size, unit)
73
74
75 def dir_size(directory):
76     """
77     Calculate a total size of the given directory.
78
79     Args:
80         directory - the given directory
81
82     Returns:
83         Size of the directory.
84     """
85
86     logger.debug('Get the size of %s', directory)
87
88     total_size = 0
89     for root, _, files in os.walk(directory):
90         for file in files:
91             itempath = os.path.join(root, file)
92             if os.path.islink(itempath):
93                 continue
94
95             total_size += os.path.getsize(itempath)
96
97     logger.debug('The size of %s: %s', directory, total_size)
98     return total_size
99
100
101 def dir_size_str(directory, format_in_bytes=False):
102     """
103     Calculate a total size of the given directory and format as a string.
104
105     Args:
106         directory - the given directory
107         format_in_bytes - if True, size is expressed in bytes,
108             otherwise human readable units are used.
109
110     Returns:
111         Size of the directory as a formatted string.
112     """
113
114     size = dir_size(directory)
115     size_str = size_to_str(size, format_in_bytes)
116
117     logger.debug('The size of %s: %s', directory, size_str)
118     return size_str
119
120
121 def size_bylist(root_dir, salomexc):
122     """
123     Calcualate the total size of files listed in the given salomexc file.
124
125     Args:
126         root_dir - a root dir for listed files
127         salomexc - file that contents a list of files.
128
129     Returns:
130         The total size of listed files.
131     """
132
133     logger.debug('Calcualate the total size of files inside %s listed in %s...',
134         root_dir, salomexc)
135
136     try:
137         with open(salomexc, 'r', encoding='UTF-8') as file:
138             total_size = 0
139             for line in file:
140                 path_to_file = os.path.join(root_dir, line.strip())
141                 #logger.debug('Check the file %s...', path_to_file)
142
143                 if os.path.isfile(path_to_file):
144                     size = os.path.getsize(path_to_file)
145                     total_size += size
146                     logger.debug('%s size: %s', path_to_file, size)
147
148                 elif os.path.islink(path_to_file):
149                     logger.debug('%s is link. Skip.', path_to_file)
150                     continue
151
152                 elif os.path.isdir(path_to_file):
153                     logger.warning('Directories are not expected to be listed in %s file! '
154                         'Skip.',
155                         salomexc)
156                     continue
157
158                 else:
159                     logger.warning('Unexpected path %s '
160                         'is not a file, link or directory. Skip.',
161                         path_to_file)
162
163             return total_size
164
165     except OSError:
166         logger.error(format_exc())
167
168     return None
169
170
171 def ext_size(install_dir, salomex_name):
172     """
173     Calculate a total size of a salome extension from SALOME install root.
174
175     Args:
176         salome_root - path to SALOME install root directory.
177         salomex_name - a name of the given salome extension.
178
179     Returns:
180         Size of the directory in bytes.
181     """
182
183     # Check if provided dirname is valid
184     if not isvalid_dirname(install_dir):
185         return None
186
187     # Check if an extension with this name is installed
188     salomexc = find_salomexc(install_dir, salomex_name)
189     if not salomexc:
190         logger.error('Cannot calculate the size of %s extension!',
191             salomex_name)
192         return None
193
194     # Calculate the size
195     return size_bylist(os.path.join(install_dir, SALOME_EXTDIR), salomexc)
196
197
198 def ext_size_str(install_dir, salomex_name, format_in_bytes=False):
199     """
200     Calculate a total size of the given extension and format as a string.
201
202     Args:
203         install_dir - directory where the given extension is installed.
204         salomex_name - the given extension's name.
205         format_in_bytes - if True, size is expressed in bytes,
206             otherwise human readable units are used.
207
208     Returns:
209         Size of the extension as a formatted string.
210     """
211
212     size = ext_size(install_dir, salomex_name)
213     if size is None:
214         return ''
215
216     size_str = size_to_str(size, format_in_bytes)
217
218     logger.debug('The size of %s: %s', salomex_name, size_str)
219     return size_str
220
221
222 def dependency_tree(directory):
223     r"""
224     Create a dependency tree for all salome extensions
225     installed in the given directory.
226
227     Args:
228         directory - the given directory
229
230     Returns:
231         A dictionary like that for extensions A, B, C, D and E:
232           A
233          /|\
234         / B D
235         \/ \/
236         C   E
237
238         { 'A': ['B', 'C', 'D'], 'B': ['C', 'E'], 'C': [], 'D': ['E'], 'E': [] }.
239     """
240
241     logger.debug('Build dependency tree for extensions in %s', directory)
242
243     tree = {}
244     salomexd_files = list_files_ext(directory, DFILE_EXT)
245     logger.debug('There are %s extensions in %s', len(salomexd_files), directory)
246
247     for salomexd_file in salomexd_files:
248         ext_name, _ = os.path.splitext(os.path.basename(salomexd_file))
249
250         logger.debug('Check dependencies for %s...', salomexd_file)
251         salomexd_content = read_salomexd(salomexd_file)
252
253         if EXTDEPENDSON_KEY in salomexd_content:
254             depends_on = salomexd_content[EXTDEPENDSON_KEY]
255             logger.debug('List of dependencies: %s', depends_on)
256
257             tree[ext_name] = depends_on
258
259     logger.debug('Dependency tree: %s', tree)
260     return tree
261
262
263 if __name__ == '__main__':
264     if len(sys.argv) == 2:
265         dir_size_str(sys.argv[1])
266         dependency_tree(sys.argv[1])
267     elif len(sys.argv) == 3:
268         arg_1, arg_2 = sys.argv[1:] # pylint: disable=unbalanced-tuple-unpacking
269         ext_size_str(arg_1, arg_2)
270     else:
271         logger.error('You must provide all the arguments!')
272         logger.info(dir_size_str.__doc__)
273         logger.info(dependency_tree.__doc__)
274         logger.info(ext_size_str.__doc__)