]> SALOME platform Git repositories - modules/kernel.git/blob - bin/SalomeOnDemandTK/extension_utilities.py
Salome HOME
[bos #32522][EDF] SALOME on Demand. Added extension_remover module.
[modules/kernel.git] / bin / SalomeOnDemandTK / extension_utilities.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_utilities.py
26 #  Author : Konstantin Leontev, Open Cascade
27 #
28 #  @package SalomeOnDemandTK
29 #  @brief Set of utility functions those help to build SALOME python extensions.
30
31 """
32 Utilities and constants those help deal with salome extension files.
33 """
34
35 import os
36 import logging
37 import json
38 from traceback import format_exc
39
40 # Setup logger's output
41 FORMAT = '%(funcName)s():%(lineno)s: %(message)s'
42 logging.basicConfig(format=FORMAT, level=logging.DEBUG)
43 logger = logging.getLogger()
44
45 SALOME_EXTDIR = '__SALOME_EXT__'
46 ARCFILE_EXT = 'salomex'
47 BFILE_EXT = 'salomexb'
48 CFILE_EXT = 'salomexc'
49 DFILE_EXT = 'salomexd'
50 PYFILE_EXT = 'py'
51 ENVPYFILE_SUF = '_env.py'
52
53 EXTNAME_KEY = 'name'
54 EXTDESCR_KEY = 'descr'
55 EXTDEPENDSON_KEY = 'depends_on'
56 EXTAUTHOR_KEY = 'author'
57 EXTCOMPONENT_KEY = 'components'
58
59
60 def create_salomexd(name, descr='', depends_on=None, author='', components=None):
61     """
62     Create a basic salomexd file from provided args.
63     Current version is a json file with function args as the keys.
64
65     Args:
66         name - the name of the corresponding salome extension.
67         depends_on - list of the modules that current extension depends on.
68         author - an author of the extension.
69         components - the names of the modules those current extension was built from.
70
71     Raises:
72         Raises OSError exception.
73
74     Returns:
75         None
76     """
77
78     logger.debug('Create salomexd file...')
79
80     if depends_on is None:
81         depends_on = []
82
83     if components is None:
84         components = []
85
86     json_string = json.dumps(
87         {
88             EXTNAME_KEY: name,
89             EXTDESCR_KEY: descr,
90             EXTDEPENDSON_KEY: depends_on,
91             EXTAUTHOR_KEY: author,
92             EXTCOMPONENT_KEY: components
93         },
94         indent=4
95     )
96
97     try:
98         with open(name + '.' + DFILE_EXT, "w", encoding="utf-8") as file:
99             file.write(json_string)
100
101     except OSError:
102         logger.error(format_exc())
103
104
105 def read_salomexd(file_path):
106     """
107     Reads a content of a salomexd file. Current version is a json file.
108     There's no check if the file_path is a valid salomexd file name.
109     It's expected that user call isvalid_filename() in advance.
110
111     Args:
112         file_path - the path to the salomexd file.
113
114     Raises:
115         Raises OSError exception.
116
117     Returns:
118         A dictionary that represents the content of the salomexd file.
119     """
120
121     logger.debug('Read salomexd file %s', file_path)
122
123     try:
124         with open(file_path, 'r', encoding='UTF-8') as file:
125             return json.load(file)
126
127     except OSError:
128         logger.error(format_exc())
129         return {}
130
131
132 def create_salomexb(name, included):
133     """
134     Create a salomexb file from a list of included file names.
135     For example:
136     */*.py
137     doc/*.html
138     doc/*.jp
139
140     Args:
141         name - the name of the corresponding salome extension.
142         included - list of the directories those must be included inside a salomex.
143
144     Raises:
145         Raises OSError exception.
146
147     Returns:
148         None
149     """
150
151     logger.debug('Create salomexb file...')
152
153     try:
154         with open(name + '.' + BFILE_EXT, "w", encoding="utf-8") as file:
155             file.write('\n'.join(included[0:]))
156
157     except OSError:
158         logger.error(format_exc())
159
160
161 def read_salomexb(file_path):
162     """
163     Returns a content af a salomexb file as a list of strings.
164     There's no check if the file_path is a valid salomexb file name.
165     It's expected that user call isvalid_filename() in advance.
166
167     Args:
168         file_path - the path to the salomexb file.
169
170     Raises:
171         Raises OSError exception.
172
173     Returns:
174         List of strings - paths to the directories those must be included in
175         corresponding salomex archive file.
176     """
177
178     logger.debug('Read salomexb file %s', file_path)
179
180     try:
181         with open(file_path, 'r', encoding='UTF-8') as file:
182             return [line.rstrip() for line in file]
183
184     except OSError:
185         logger.error(format_exc())
186         return []
187
188
189 def list_files(dir_path):
190     """
191     Returns the recursive list of relative paths to files as strings
192     in the dir_path root directory and all subdirectories.
193
194     Args:
195         dir_path - the path to the directory where you search for files.
196
197     Raises:
198         Raises OSError exception.
199
200     Returns:
201         A list of relative paths to files inside the given directory.
202     """
203
204     files_list = []
205     for root, _, files in os.walk(dir_path):
206         for file in files:
207             files_list += os.path.relpath(os.path.join(root, file), dir_path)
208
209     return files_list
210
211
212 def list_files_filter(dir_path, filter_patterns):
213     """
214     Returns the recursive list of relative paths to files as strings
215     in the dir_path root directory and all subdirectories.
216
217     Args:
218         dir_path - the path to the directory where you search for files.
219         filter_patterns - list of expressions for matching file names.
220
221     Returns:
222         files_abs - a list of absolute paths to selected files.
223         files_rel - a list of relative paths to selected files.
224     """
225
226     logger.debug('Get list of files to add into archive...')
227
228     files_abs = []
229     files_rel = []
230
231     for root, _, files in os.walk(dir_path):
232         for file in files:
233             for pattern in filter_patterns:
234                 filename_abs = os.path.join(root, file)
235                 filename_rel = os.path.relpath(filename_abs, dir_path)
236
237                 if filename_rel.startswith(pattern):
238                     logger.debug('File name %s matches pattern %s', filename_rel, pattern)
239                     files_abs.append(filename_abs)
240                     files_rel.append(filename_rel)
241
242     return files_abs, files_rel
243
244
245 def list_files_ext(dir_path, ext):
246     """
247     Returns a list of abs paths to files with a given extension
248     in the dir_path directory.
249
250     Args:
251         dir_path - the path to the directory where you search for files.
252         ext - a given extension.
253
254     Returns:
255         A list of absolute paths to selected files.
256     """
257
258     logger.debug('Get list of files with extension %s...', ext)
259
260     dot_ext = '.' + ext
261     return [os.path.join(dir_path, f) for f in os.listdir(dir_path) if f.endswith(dot_ext)]
262
263
264 def list_tonewline_str(str_list):
265     """
266     Converts the given list of strings to a newline separated string.
267
268     Args:
269         dir_path - the path to the directory where you search for files.
270
271     Returns:
272         A newline separated string.
273     """
274     return '\n'.join(file for file in str_list)
275
276
277 def isvalid_filename(filename, extension):
278     """
279     Checks if a given filename is valid in a sense that it exists and have a given extension.
280
281     Args:
282         filename - the name of the file to check.
283         extension - expected file name extension.
284
285     Returns:
286         True if the given filename is valid for given extension.
287     """
288
289     logger.debug('Check if the filename %s exists and has an extension %s', filename, extension)
290
291     # First do we even have something to check here
292     if filename == '' or extension == '':
293         logger.error('A filename and extension cannot be empty! Args: filename=%s, extension=%s',
294             filename, extension)
295         return False
296
297     # Check if the filename matchs the provided extension
298     _, ext = os.path.splitext(filename)
299     ext = ext.lstrip('.')
300     if ext != extension:
301         logger.error('The filename %s doesnt have a valid extension! \
302             The valid extension must be: %s, but get: %s',
303             filename, extension, ext)
304         return False
305
306     # Check if the file base name is not empty
307     base_name = os.path.basename(filename)
308     if base_name == '':
309         logger.error('The file name %s has an empty base name!', filename)
310         return False
311
312     # Check if a file with given filename exists
313     if not os.path.isfile(filename):
314         logger.error('The filename %s is not an existing regular file!', filename)
315         return False
316
317     logger.debug('Filename %s exists and has an extension %s', filename, extension)
318     return True
319
320
321 def isvalid_dirname(dirname):
322     """
323     Checks if a given directory name exists.
324
325     Args:
326         dirname - the name of the directory to check.
327
328     Returns:
329         True if the given dirname is valid.
330     """
331
332     logger.debug('Check if the dirname %s exists', dirname)
333
334     # First do we even have something to check here
335     if dirname == '':
336         logger.error('A dirname argument cannot be empty! dirname=%s', dirname)
337         return False
338
339     # Check if a file with given filename exists
340     if not os.path.isdir(dirname):
341         logger.error('The dirname %s is not an existing regular file!', dirname)
342         return False
343
344     logger.debug('Directory %s exists', dirname)
345     return True
346
347
348 def list_dependants(install_dir, salomex_name):
349     """
350     Checks if we have installed extensions those depend on a given extension.
351
352     Args:
353         install_dir - path to SALOME install root directory.
354         salomex_name - a name of salome extension to check.
355
356     Returns:
357         True if the given extension has dependants.
358     """
359
360     logger.debug('Check if there are other extensions that depends on %s...', salomex_name)
361     dependants = []
362     salomexd_files = list_files_ext(install_dir, DFILE_EXT)
363
364     for salomexd_file in salomexd_files:
365         logger.debug('Check dependencies for %s...', salomexd_file)
366         salomexd_content = read_salomexd(salomexd_file)
367
368         if EXTDEPENDSON_KEY in salomexd_content and salomexd_content[EXTDEPENDSON_KEY]:
369             depends_on = salomexd_content[EXTDEPENDSON_KEY]
370             logger.debug('List of dependencies: %s', depends_on)
371
372             if salomex_name in depends_on:
373                 dependant_name = None
374                 if EXTNAME_KEY in salomexd_content and salomexd_content[EXTNAME_KEY]:
375                     dependant_name = salomexd_content[EXTNAME_KEY]
376                 else:
377                     logger.warning('%s file doesn\'t have %s key! '
378                         'Get an extension name from the filename.',
379                         salomexd_file, EXTNAME_KEY)
380                     dependant_name, _ = os.path.splitext(os.path.basename(salomexd_file))
381
382                 dependants.append(dependant_name)
383
384     if len(dependants) > 0:
385         logger.debug('An extension %s has followed extensions those depend on it: %s',
386             salomex_name, dependants)
387
388     return dependants
389
390
391 def is_empty_dir(directory):
392     """
393     Checks if the given directory is empty.
394
395     Args:
396         directory - path to directory to check.
397
398     Returns:
399         True if the given directory is empty.
400     """
401
402     return not next(os.scandir(directory), None)